diff --git a/src/_secureaccesstokenauthorizer/_storage_client.py b/src/_secureaccesstokenauthorizer/_storage_client.py index bfdf7b9930474b2e83464f728f67d31585b83c4f..9a77fbcf87a69c34fde371d6c4cea4fa7e7c3c8d 100644 --- a/src/_secureaccesstokenauthorizer/_storage_client.py +++ b/src/_secureaccesstokenauthorizer/_storage_client.py @@ -103,3 +103,17 @@ class SecureAccessTokenAuthorizerStorageClient(object): renew_secret, ) + def advise_corrupt_share( + self, + share_type, + storage_index, + shnum, + reason, + ): + return self._rref.callRemote( + "advise_corrupt_share", + share_type, + storage_index, + shnum, + reason, + ) diff --git a/src/_secureaccesstokenauthorizer/_storage_server.py b/src/_secureaccesstokenauthorizer/_storage_server.py index 0943681b5c5bff41eca5b23172b502539d19c175..6264b310a93065b93c112546721038d8797a87bb 100644 --- a/src/_secureaccesstokenauthorizer/_storage_server.py +++ b/src/_secureaccesstokenauthorizer/_storage_server.py @@ -123,6 +123,9 @@ class SecureAccessTokenAuthorizerStorageServer(Referenceable): self._validate_tokens(tokens) return self._original.remote_renew_lease(*a, **kw) + def remote_advise_corrupt_share(self, *a, **kw): + return self._original.remote_advise_corrupt_share(*a, **kw) + # I don't understand why this is required. # SecureAccessTokenAuthorizerStorageServer is-a Referenceable. It seems like # the built in adapter should take care of this case. diff --git a/src/_secureaccesstokenauthorizer/tests/test_storage_protocol.py b/src/_secureaccesstokenauthorizer/tests/test_storage_protocol.py index 35bd82de39c649887b4fc6096602347a9412a13b..b4f341bab721f2a9833877ec40edfee89e244231 100644 --- a/src/_secureaccesstokenauthorizer/tests/test_storage_protocol.py +++ b/src/_secureaccesstokenauthorizer/tests/test_storage_protocol.py @@ -275,6 +275,47 @@ class ImmutableTests(TestCase): Equals(int(now + RENEW_INTERVAL)), ) + @given( + storage_index=storage_indexes(), + renew_secret=lease_renew_secrets(), + cancel_secret=lease_cancel_secrets(), + sharenum=sharenums(), + size=sizes(), + ) + def test_advise_corrupt_share(self, storage_index, renew_secret, cancel_secret, sharenum, size): + """ + An advisory of corruption in a share can be sent to the server. + """ + # Hypothesis causes our storage server to be used many times. Clean + # up between iterations. + cleanup_storage_server(self.anonymous_storage_server) + + # Create a share we can toy with. + _, allocated = self.anonymous_storage_server.remote_allocate_buckets( + storage_index, + renew_secret, + cancel_secret, + {sharenum}, + size, + canary=self.canary, + ) + [(_, writer)] = allocated.items() + writer.remote_write(0, bytes_for_share(sharenum, size)) + writer.remote_close() + + extract_result( + self.client.advise_corrupt_share( + u"immutable", + storage_index, + sharenum, + u"the bits look bad", + ), + ) + self.assertThat( + FilePath(self.anonymous_storage_server.corruption_advisory_dir).children(), + HasLength(1), + ) + def get_leases(storage_server, storage_index): """ @@ -304,7 +345,11 @@ def cleanup_storage_server(storage_server): :param allmydata.storage.server.StorageServer storage_server: The storage server with some on-disk shares to delete. """ - start = FilePath(storage_server.sharedir) - for p in start.walk(): - if p is not start: - p.remove() + starts = [ + FilePath(storage_server.sharedir), + FilePath(storage_server.corruption_advisory_dir), + ] + for start in starts: + for p in start.walk(): + if p is not start: + p.remove()