From efca3f6de3cab74374e55e89e34d4ef4eca9235c Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone <exarkun@twistedmatrix.com> Date: Fri, 5 Jul 2019 13:39:51 -0400 Subject: [PATCH] renew_lease --- .../_storage_client.py | 13 +++ .../_storage_server.py | 4 + .../tests/test_storage_protocol.py | 83 ++++++++++++++++--- 3 files changed, 90 insertions(+), 10 deletions(-) diff --git a/src/_secureaccesstokenauthorizer/_storage_client.py b/src/_secureaccesstokenauthorizer/_storage_client.py index f1df274..bfdf7b9 100644 --- a/src/_secureaccesstokenauthorizer/_storage_client.py +++ b/src/_secureaccesstokenauthorizer/_storage_client.py @@ -90,3 +90,16 @@ class SecureAccessTokenAuthorizerStorageClient(object): renew_secret, cancel_secret, ) + + def renew_lease( + self, + storage_index, + renew_secret, + ): + return self._rref.callRemote( + "renew_lease", + self._get_tokens(), + storage_index, + renew_secret, + ) + diff --git a/src/_secureaccesstokenauthorizer/_storage_server.py b/src/_secureaccesstokenauthorizer/_storage_server.py index 77ab53b..0943681 100644 --- a/src/_secureaccesstokenauthorizer/_storage_server.py +++ b/src/_secureaccesstokenauthorizer/_storage_server.py @@ -119,6 +119,10 @@ class SecureAccessTokenAuthorizerStorageServer(Referenceable): self._validate_tokens(tokens) return self._original.remote_add_lease(*a, **kw) + def remote_renew_lease(self, tokens, *a, **kw): + self._validate_tokens(tokens) + return self._original.remote_renew_lease(*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 96369af..35bd82d 100644 --- a/src/_secureaccesstokenauthorizer/tests/test_storage_protocol.py +++ b/src/_secureaccesstokenauthorizer/tests/test_storage_protocol.py @@ -17,13 +17,10 @@ Tests for communication between the client and server components. """ import attr -from struct import ( - unpack, -) - from fixtures import ( Fixture, TempDir, + MonkeyPatch, ) from testtools import ( TestCase, @@ -225,14 +222,80 @@ class ImmutableTests(TestCase): cancel_secret, ), ) - - # It's hard to assert much about the lease without knowing about - # *some* implementation details of the storage server. I prefer to - # know Python API details rather than on-disk format details. - [(_, reader)] = self.server.remote_get_buckets(storage_index).items() - leases = list(reader._share_file.get_leases()) + [(_, leases)] = get_leases(self.server, storage_index).items() self.assertThat(leases, HasLength(2)) + @given( + storage_index=storage_indexes(), + renew_secret=lease_renew_secrets(), + cancel_secret=lease_cancel_secrets(), + sharenum=sharenums(), + size=sizes(), + ) + def test_renew_lease(self, storage_index, renew_secret, cancel_secret, sharenum, size): + """ + A lease on an immutable share can be updated to expire at a later time. + """ + # Hypothesis causes our storage server to be used many times. Clean + # up between iterations. + cleanup_storage_server(self.anonymous_storage_server) + + # Take control of time (in this hacky, fragile way) so we can verify + # the expiration time gets bumped by the renewal. + now = 1000000000.5 + self.useFixture(MonkeyPatch("time.time", lambda: now)) + + # 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() + + now += 100000 + extract_result( + self.client.renew_lease( + storage_index, + renew_secret, + ), + ) + + # Based on Tahoe-LAFS' hard-coded renew time. + RENEW_INTERVAL = 60 * 60 * 24 * 31 + + [(_, [lease])] = get_leases(self.server, storage_index).items() + self.assertThat( + lease.get_expiration_time(), + Equals(int(now + RENEW_INTERVAL)), + ) + + +def get_leases(storage_server, storage_index): + """ + Get all leases for all shares of the given storage index on the given + server. + + :param StorageServer storage_server: The storage server on which to find + the information. + + :param bytes storage_index: The storage index for which to look up shares. + + :return dict[int, list[LeaseInfo]]: The lease information for each share. + """ + # It's hard to assert much about the lease without knowing about *some* + # implementation details of the storage server. I prefer to know Python + # API details rather than on-disk format details. + return { + sharenum: list(reader._share_file.get_leases()) + for (sharenum, reader) + in storage_server.remote_get_buckets(storage_index).items() + } def cleanup_storage_server(storage_server): """ -- GitLab