From 21ee8e8e3a2d663bd4ba8f40a48866ec8a71d656 Mon Sep 17 00:00:00 2001
From: Jean-Paul Calderone <exarkun@twistedmatrix.com>
Date: Fri, 20 Dec 2019 16:11:24 -0500
Subject: [PATCH] [wip] start keeping track of when it ran

---
 src/_zkapauthorizer/_plugin.py                |  7 +-
 src/_zkapauthorizer/lease_maintenance.py      | 65 +++++++++++++++++--
 .../tests/test_lease_maintenance.py           | 14 +++-
 3 files changed, 74 insertions(+), 12 deletions(-)

diff --git a/src/_zkapauthorizer/_plugin.py b/src/_zkapauthorizer/_plugin.py
index 81d7479..018c096 100644
--- a/src/_zkapauthorizer/_plugin.py
+++ b/src/_zkapauthorizer/_plugin.py
@@ -210,7 +210,7 @@ def _maybe_attach_maintenance_service(reactor, client_node):
             client_node.config,
             client_node,
         ).setServiceParent(client_node)
-    except Exception as e:
+    except Exception:
         _log.failure("Attaching maintenance service to client node")
     else:
         _log.info("Found existing lease maintenance service")
@@ -234,7 +234,7 @@ def _create_maintenance_service(reactor, node_config, client_node):
     # called.
     maintain_leases = maintain_leases_from_root(
         client_node.create_node_from_uri(
-            node_config.get_private_config(u"rootcap"),
+            node_config.get_private_config(b"rootcap"),
         ),
         client_node.get_storage_broker(),
         client_node._secret_holder,
@@ -242,10 +242,11 @@ def _create_maintenance_service(reactor, node_config, client_node):
         timedelta(days=3),
         get_now,
     )
+    last_run_path = FilePath(node_config.get_private_path(b"last-lease-maintenance-run"))
     # Create the service to periodically run the lease maintenance operation.
     return lease_maintenance_service(
         maintain_leases,
         reactor,
-        node_config.get_private_config(u"last-lease-maintenance-run", None),
+        last_run_path,
         random,
     )
diff --git a/src/_zkapauthorizer/lease_maintenance.py b/src/_zkapauthorizer/lease_maintenance.py
index 6210087..2372745 100644
--- a/src/_zkapauthorizer/lease_maintenance.py
+++ b/src/_zkapauthorizer/lease_maintenance.py
@@ -24,9 +24,15 @@ from datetime import (
     datetime,
     timedelta,
 )
-
+from errno import (
+    ENOENT,
+)
 import attr
 
+from aniso8601 import (
+    parse_datetime,
+)
+
 from twisted.internet.defer import (
     inlineCallbacks,
     maybeDeferred,
@@ -46,6 +52,10 @@ from allmydata.util.hashutil import (
     bucket_renewal_secret_hash,
 )
 
+from .controller import (
+    bracket,
+)
+
 SERVICE_NAME = u"lease maintenance service"
 
 @inlineCallbacks
@@ -276,7 +286,7 @@ class _FuzzyTimerService(Service):
 def lease_maintenance_service(
         maintain_leases,
         reactor,
-        last_run,
+        last_run_path,
         random,
         interval_mean=None,
         interval_range=None,
@@ -288,9 +298,12 @@ def lease_maintenance_service(
     :param IReactorClock reactor: A Twisted reactor for scheduling renewal
         activity.
 
-    :param datetime last_run: The time at which lease maintenance last ran to
-        inform an adjustment to the first interval before running it again, or
-        ``None`` not to make such an adjustment.
+    :param FilePath last_run_path: A path containing the time (as an ISO8601
+        datetime string) at which lease maintenance last ran to inform an
+        adjustment to the first interval before running it again.  If no file
+        exists at the path it is treated as though there has been no previous
+        run.  The path will also be rewritten on each run to update this
+        value.
 
     :param random: An object like ``random.Random`` which can be used as a
         source of scheduling delay.
@@ -316,6 +329,7 @@ def lease_maintenance_service(
                 (interval_mean + halfrange).total_seconds(),
             ),
         )
+    last_run = read_time_from_path(last_run_path)
     if last_run is None:
         initial_interval = sample_interval_distribution()
     else:
@@ -329,15 +343,54 @@ def lease_maintenance_service(
             timedelta(0),
         )
 
+
     return _FuzzyTimerService(
         SERVICE_NAME,
-        maintain_leases,
+        bracket(
+            lambda: None,
+            lambda: write_time_to_path(
+                last_run_path,
+                datetime.utcfromtimestamp(reactor.seconds()),
+            ),
+            maintain_leases,
+        ),
         initial_interval,
         sample_interval_distribution,
         reactor,
     )
 
 
+def write_time_to_path(path, when):
+    """
+    Write an ISO8601 datetime string to a file.
+
+    :param FilePath path: The path to a file to which to write the datetime
+        string.
+
+    :param datetime when: The datetime to write.
+    """
+    path.setContent(when.isoformat())
+
+
+def read_time_from_path(path):
+    """
+    Read an ISO8601 datetime string from a file.
+
+    :param FilePath path: The path to a file containing a datetime string.
+
+    :return: None if no file exists at the path.  Otherwise, a datetime
+        instance giving the time represented in the file.
+    """
+    try:
+        when = path.getContent()
+    except IOError as e:
+        if ENOENT == e.errno:
+            return None
+        raise
+    else:
+        return parse_datetime(when)
+
+
 def visit_storage_indexes_from_root(visitor, root_node):
     """
     An operation for ``lease_maintenance_service`` which applies the given
diff --git a/src/_zkapauthorizer/tests/test_lease_maintenance.py b/src/_zkapauthorizer/tests/test_lease_maintenance.py
index c3c7e8b..7c023a1 100644
--- a/src/_zkapauthorizer/tests/test_lease_maintenance.py
+++ b/src/_zkapauthorizer/tests/test_lease_maintenance.py
@@ -42,6 +42,9 @@ from testtools.matchers import (
 from testtools.twistedsupport import (
     succeeded,
 )
+from fixtures import (
+    TempDir,
+)
 from hypothesis import (
     given,
     note,
@@ -58,6 +61,9 @@ from hypothesis.strategies import (
     just,
 )
 
+from twisted.python.filepath import (
+    FilePath,
+)
 from twisted.internet.task import (
     Clock,
 )
@@ -201,7 +207,7 @@ class LeaseMaintenanceServiceTests(TestCase):
         service = lease_maintenance_service(
             dummy_maintain_leases,
             clock,
-            None,
+            FilePath(self.useFixture(TempDir()).join(u"last-run")),
             random,
         )
         self.assertThat(
@@ -229,7 +235,7 @@ class LeaseMaintenanceServiceTests(TestCase):
         service = lease_maintenance_service(
             dummy_maintain_leases,
             clock,
-            None,
+            FilePath(self.useFixture(TempDir()).join(u"last-run")),
             random,
             mean,
             range_,
@@ -266,6 +272,8 @@ class LeaseMaintenanceServiceTests(TestCase):
 
         # Figure out the absolute last run time.
         last_run = datetime_now - since_last_run
+        last_run_path = FilePath(self.useFixture(TempDir()).join(u"last-run"))
+        last_run_path.setContent(last_run.isoformat())
 
         service = lease_maintenance_service(
             dummy_maintain_leases,
@@ -314,7 +322,7 @@ class LeaseMaintenanceServiceTests(TestCase):
         service = lease_maintenance_service(
             maintain_leases,
             clock,
-            None,
+            FilePath(self.useFixture(TempDir()).join(u"last-run")),
             random,
         )
         service.startService()
-- 
GitLab