From 44cec12c285c1cefc73c258b6de47b0bb970ceb6 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone <exarkun@twistedmatrix.com> Date: Sat, 19 Feb 2022 16:27:47 -0500 Subject: [PATCH] start on a Tahoe-LAFS downloader This test is bogus, though, because the Tahoe-LAFS web API test double misbehaves for directory entry requests. --- src/_zkapauthorizer/recover.py | 51 +++++++++++++++++++++++ src/_zkapauthorizer/tests/test_recover.py | 46 +++++++++++++++++++- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/_zkapauthorizer/recover.py b/src/_zkapauthorizer/recover.py index e5b1580..8b570ba 100644 --- a/src/_zkapauthorizer/recover.py +++ b/src/_zkapauthorizer/recover.py @@ -19,7 +19,19 @@ from io import BytesIO from sqlite3 import Cursor from typing import BinaryIO, Callable, Dict, Iterator, Optional +from allmydata.node import _Config from attrs import define +from hyperlink import DecodedURL +from treq.client import HTTPClient +from twisted.python.filepath import FilePath + +from .tahoe import download + + +class SnapshotMissing(Exception): + """ + No snapshot was not found in the replica directory. + """ class AlreadyRecovering(Exception): @@ -183,3 +195,42 @@ def recover(statements: Iterator[str], cursor) -> None: """ for sql in statements: cursor.execute(sql) + + +async def tahoe_lafs_downloader( + treq: HTTPClient, + node_config: _Config, + recovery_cap: str, + set_state: SetState, +) -> Awaitable: # Awaitable[FilePath] + """ + Download replica data from the given replica directory capability into the + node's private directory. + """ + api_root = DecodedURL.from_text( + FilePath(node_config.get_config_path("node.url")).getContent().decode("ascii") + ) + snapshot_path = FilePath(node_config.get_private_path("snapshot.sql")) + + set_state(RecoveryState(stage=RecoveryStages.downloading)) + await download(treq, snapshot_path, api_root, recovery_cap, ["snapshot.sql"]) + return snapshot_path + + +def get_tahoe_lafs_downloader( + httpclient: HTTPClient, node_config: _Config +) -> Callable[[str], Downloader]: + """ + Bind some parameters to ``tahoe_lafs_downloader`` in a convenient way. + + :return: A callable that accepts a Tahoe-LAFS capability string and + returns a downloader for that capability. + """ + + def get_downloader(cap_str): + def downloader(set_state): + return tahoe_lafs_downloader(httpclient, node_config, cap_str, set_state) + + return downloader + + return get_downloader diff --git a/src/_zkapauthorizer/tests/test_recover.py b/src/_zkapauthorizer/tests/test_recover.py index a92dad8..e68aea2 100644 --- a/src/_zkapauthorizer/tests/test_recover.py +++ b/src/_zkapauthorizer/tests/test_recover.py @@ -5,7 +5,9 @@ Tests for ``_zkapauthorizer.recover``, the replication recovery system. from sqlite3 import Connection, connect from typing import Dict, Iterator -from hypothesis import assume, note, settings +from allmydata.testing.web import create_fake_tahoe_root, create_tahoe_treq_client +from fixtures import TempDir +from hypothesis import assume, given, note, settings from hypothesis.stateful import ( RuleBasedStateMachine, invariant, @@ -24,18 +26,27 @@ from testtools.matchers import ( ) from testtools.twistedsupport import failed, succeeded from twisted.internet.defer import Deferred +from twisted.python.filepath import FilePath from ..recover import ( AlreadyRecovering, RecoveryStages, StatefulRecoverer, + get_tahoe_lafs_downloader, make_canned_downloader, make_fail_downloader, noop_downloader, recover, ) from .sql import Table, create_table -from .strategies import deletes, inserts, sql_identifiers, tables, updates +from .strategies import ( + deletes, + inserts, + sql_identifiers, + tables, + tahoe_configs, + updates, +) def snapshot(connection: Connection) -> Iterator[str]: @@ -261,3 +272,34 @@ class StatefulRecovererTests(TestCase): ), ), ) + + +class TahoeLAFSDownloaderTests(TestCase): + """ + Tests for ``get_tahoe_lafs_downloader`` and ``tahoe_lafs_downloader``. + """ + + @given(tahoe_configs()) + def test_get_downloader(self, get_config): + """ + ``get_tahoe_lafs_downloader`` returns a downloader factory that can be + used to download objects using a Tahoe-LAFS client. + """ + tempdir = self.useFixture(TempDir()) + nodedir = FilePath(tempdir.join("node")) + config = get_config(nodedir.path, "tub.port") + # The downloader wants to figure out the node's api root. + nodedir.child("private").makedirs() + nodedir.child("node.url").setContent(b"http://localhost/") + root = create_fake_tahoe_root() + cap_str = root.add_data(b"URI:DIR2-RO:", b"snapshot data").decode("ascii") + httpclient = create_tahoe_treq_client(root) + get_downloader = get_tahoe_lafs_downloader(httpclient, config) + download = get_downloader(cap_str) + + self.assertThat( + Deferred.fromCoroutine(download(lambda state: None)), + succeeded( + AfterPreprocessing(lambda fp: fp.getContent(), Equals(b"snapshot data")) + ), + ) -- GitLab