diff --git a/src/_zkapauthorizer/_plugin.py b/src/_zkapauthorizer/_plugin.py index 82d996f7c760bdee4ed6b72e0823ee43cb2b2050..4ba84ddd840e6a56e7cf54fd0ed6ae8bc3449f40 100644 --- a/src/_zkapauthorizer/_plugin.py +++ b/src/_zkapauthorizer/_plugin.py @@ -25,6 +25,10 @@ from datetime import ( datetime, timedelta, ) +from functools import ( + partial, +) + import attr from zope.interface import ( @@ -45,6 +49,9 @@ from allmydata.interfaces import ( IFoolscapStoragePlugin, IAnnounceableStorageServer, ) +from allmydata.node import ( + MissingConfigEntry, +) from allmydata.client import ( _Client, ) @@ -238,9 +245,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( - lambda: client_node.create_node_from_uri( - node_config.get_private_config(b"rootcap"), - ), + partial(get_root_nodes, client_node, node_config), client_node.get_storage_broker(), client_node._secret_holder, # Make this configuration @@ -256,3 +261,12 @@ def _create_maintenance_service(reactor, node_config, client_node): last_run_path, random, ) + + +def get_root_nodes(client_node, node_config): + try: + rootcap = node_config.get_private_config(b"rootcap") + except MissingConfigEntry: + return [] + else: + return [client_node.create_node_from_uri(rootcap)] diff --git a/src/_zkapauthorizer/lease_maintenance.py b/src/_zkapauthorizer/lease_maintenance.py index 4289232b0e3958ca097681f0388076633fa2836b..8a3a0956660984a5eb1b5d05cf8fb04759f6870d 100644 --- a/src/_zkapauthorizer/lease_maintenance.py +++ b/src/_zkapauthorizer/lease_maintenance.py @@ -69,7 +69,7 @@ SERVICE_NAME = u"lease maintenance service" @inlineCallbacks -def visit_storage_indexes(root_node, visit): +def visit_storage_indexes(root_nodes, visit): """ Call a visitor with the storage index of ``root_node`` and that of all nodes reachable from it. @@ -82,11 +82,17 @@ 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, + if not isinstance(root_nodes, list): + raise TypeError("root_nodes must be a list, not {!r}".format( + root_nodes, )) - stack = [root_node] + for node in root_nodes: + if not IFilesystemNode.providedBy(node): + raise TypeError("Root nodes must provide IFilesystemNode, {!r} does not".format( + node, + )) + + stack = root_nodes[:] while stack: elem = stack.pop() visit(elem.get_storage_index()) @@ -443,7 +449,7 @@ def read_time_from_path(path): return parse_datetime(when) -def visit_storage_indexes_from_root(visitor, get_root_node): +def visit_storage_indexes_from_root(visitor, get_root_nodes): """ An operation for ``lease_maintenance_service`` which applies the given visitor to ``root_node`` and all its children. @@ -451,17 +457,17 @@ def visit_storage_indexes_from_root(visitor, get_root_node): :param visitor: A one-argument callable which takes the traversal function and which should call it as desired. - :param get_root_node: A no-argument callable which returns the filesystem - node (``IFilesystemNode``) at which traversal will begin. + :param get_root_nodes: A no-argument callable which returns a list of + filesystem nodes (``IFilesystemNode``) at which traversal will begin. :return: A no-argument callable to perform the visits. """ 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(), + # Make sure we call get_root_nodes each time to give us a chance + # to notice when it changes. + get_root_nodes(), ), ) @@ -495,7 +501,7 @@ class MemoryMaintenanceObserver(object): def maintain_leases_from_root( - get_root_node, + get_root_nodes, storage_broker, secret_holder, min_lease_remaining, @@ -507,9 +513,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 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 get_root_nodes: A no-argument callable which returns the list of + Tahoe-LAFS filesystem nodes (``IFilesystemNode``) to use as the roots + of the node hierarchies 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 @@ -539,7 +545,7 @@ def maintain_leases_from_root( return visit_storage_indexes_from_root( visitor, - get_root_node, + get_root_nodes, ) diff --git a/src/_zkapauthorizer/tests/test_lease_maintenance.py b/src/_zkapauthorizer/tests/test_lease_maintenance.py index d5dd7c530c81750a052ed9dfabf93dbd4e415f3d..4e0143ce899ee19136c8ed309e902d013d989ea6 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, - lambda: root_node, + lambda: [root_node], ) self.assertThat( @@ -516,7 +516,7 @@ class MaintainLeasesFromRootTests(TestCase): ) operation = maintain_leases_from_root( - lambda: 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( - lambda: root_node, + lambda: [root_node], storage_broker, secret_holder, min_lease_remaining,