From 0d6850f2be37f194669be85dea2aec4fad0a7b9b Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone <exarkun@twistedmatrix.com> Date: Wed, 26 Feb 2020 10:35:52 -0500 Subject: [PATCH] Allow the lease maintenance service to be created when no rootcap exists The first time the node starts there will be no rootcap and there cannot easily be one unless the node is allowed to start so one can be created. --- src/_zkapauthorizer/_plugin.py | 2 +- src/_zkapauthorizer/lease_maintenance.py | 30 ++++++++++++------- .../tests/test_lease_maintenance.py | 6 ++-- src/_zkapauthorizer/tests/test_plugin.py | 28 +++++++++++++---- 4 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/_zkapauthorizer/_plugin.py b/src/_zkapauthorizer/_plugin.py index 1ec0f75..82d996f 100644 --- a/src/_zkapauthorizer/_plugin.py +++ b/src/_zkapauthorizer/_plugin.py @@ -238,7 +238,7 @@ def _create_maintenance_service(reactor, node_config, client_node): # Create the operation which performs the lease maintenance job when # called. maintain_leases = maintain_leases_from_root( - client_node.create_node_from_uri( + lambda: client_node.create_node_from_uri( node_config.get_private_config(b"rootcap"), ), client_node.get_storage_broker(), diff --git a/src/_zkapauthorizer/lease_maintenance.py b/src/_zkapauthorizer/lease_maintenance.py index f32de8d..4289232 100644 --- a/src/_zkapauthorizer/lease_maintenance.py +++ b/src/_zkapauthorizer/lease_maintenance.py @@ -50,6 +50,7 @@ from twisted.python.log import ( from allmydata.interfaces import ( IDirectoryNode, + IFilesystemNode, ) from allmydata.util.hashutil import ( file_renewal_secret_hash, @@ -81,6 +82,10 @@ def visit_storage_indexes(root_node, visit): :return Deferred: A Deferred which fires after all nodes have been visited. """ + if not IFilesystemNode.providedBy(root_node): + raise TypeError("root_node must provide IFilesystemNode, {!r} does not".format( + root_node, + )) stack = [root_node] while stack: elem = stack.pop() @@ -438,7 +443,7 @@ def read_time_from_path(path): return parse_datetime(when) -def visit_storage_indexes_from_root(visitor, root_node): +def visit_storage_indexes_from_root(visitor, get_root_node): """ An operation for ``lease_maintenance_service`` which applies the given visitor to ``root_node`` and all its children. @@ -446,14 +451,18 @@ def visit_storage_indexes_from_root(visitor, root_node): :param visitor: A one-argument callable which takes the traversal function and which should call it as desired. - :param IFilesystemNode root_node: The filesystem node at which traversal - will begin. + :param get_root_node: A no-argument callable which returns the filesystem + node (``IFilesystemNode``) at which traversal will begin. :return: A no-argument callable to perform the visits. """ - return partial( - visitor, - partial(visit_storage_indexes, root_node), + return lambda: visitor( + partial( + visit_storage_indexes, + # Make sure we call get_root_node each time to give us a chance to + # notice when it changes. + get_root_node(), + ), ) @@ -486,7 +495,7 @@ class MemoryMaintenanceObserver(object): def maintain_leases_from_root( - root_node, + get_root_node, storage_broker, secret_holder, min_lease_remaining, @@ -498,8 +507,9 @@ def maintain_leases_from_root( and all its children and renews their leases if they have ``min_lease_remaining`` or less on them. - :param IFilesystemNode root_node: A Tahoe-LAFS filesystem node to use as - the root of a node hierarchy to be maintained. + :param get_root_node: A no-argument callable which returns the Tahoe-LAFS + filesystem node (``IFilesystemNode``) to use as the root of a node + hierarchy to be maintained. :param StorageFarmBroker storage_broker: The storage broker which can put us in touch with storage servers where shares of the nodes to maintain @@ -529,7 +539,7 @@ def maintain_leases_from_root( return visit_storage_indexes_from_root( visitor, - root_node, + get_root_node, ) diff --git a/src/_zkapauthorizer/tests/test_lease_maintenance.py b/src/_zkapauthorizer/tests/test_lease_maintenance.py index 5b90fe3..d5dd7c5 100644 --- a/src/_zkapauthorizer/tests/test_lease_maintenance.py +++ b/src/_zkapauthorizer/tests/test_lease_maintenance.py @@ -411,7 +411,7 @@ class VisitStorageIndexesFromRootTests(TestCase): operation = visit_storage_indexes_from_root( perform_visit, - root_node, + lambda: root_node, ) self.assertThat( @@ -516,7 +516,7 @@ class MaintainLeasesFromRootTests(TestCase): ) operation = maintain_leases_from_root( - root_node, + lambda: root_node, storage_broker, secret_holder, min_lease_remaining, @@ -569,7 +569,7 @@ class MaintainLeasesFromRootTests(TestCase): observers = [observer] progress = observers.pop operation = maintain_leases_from_root( - root_node, + lambda: root_node, storage_broker, secret_holder, min_lease_remaining, diff --git a/src/_zkapauthorizer/tests/test_plugin.py b/src/_zkapauthorizer/tests/test_plugin.py index c6b4e89..55c30a5 100644 --- a/src/_zkapauthorizer/tests/test_plugin.py +++ b/src/_zkapauthorizer/tests/test_plugin.py @@ -441,7 +441,7 @@ class LeaseMaintenanceServiceTests(TestCase): """ Tests for the plugin's initialization of the lease maintenance service. """ - def _created_test(self, get_config, servers_yaml): + def _created_test(self, get_config, servers_yaml, rootcap): original_tempdir = tempfile.tempdir tempdir = self.useFixture(TempDir()) @@ -455,10 +455,11 @@ class LeaseMaintenanceServiceTests(TestCase): b"servers.yaml", servers_yaml, ) - config.write_private_config( - b"rootcap", - b"dddddddd", - ) + if rootcap: + config.write_private_config( + b"rootcap", + b"dddddddd", + ) try: d = create_client_from_config(config) @@ -492,4 +493,19 @@ class LeaseMaintenanceServiceTests(TestCase): maintenance service after it has at least one storage server to connect to. """ - return self._created_test(get_config, servers_yaml) + return self._created_test(get_config, servers_yaml, rootcap=True) + + + @settings( + deadline=None, + ) + @given( + tahoe_configs_with_dummy_redeemer, + sampled_from([SERVERS_YAML, TWO_SERVERS_YAML]), + ) + def test_created_without_rootcap(self, get_config, servers_yaml): + """ + The lease maintenance service can be created even if no rootcap has yet + been written to the client's configuration directory. + """ + return self._created_test(get_config, servers_yaml, rootcap=False) -- GitLab