[tor-commits] [bridgedb/main] Use rdsys as bridgedb backend

meskio at torproject.org meskio at torproject.org
Tue Nov 23 16:01:14 UTC 2021


commit d3a4cb06bf62dfa49d9a3850c76dfe6922f82089
Author: meskio <meskio at torproject.org>
Date:   Thu Nov 4 20:00:26 2021 +0100

    Use rdsys as bridgedb backend
    
    Closes: #40031
---
 bridgedb.conf                 |   6 +
 bridgedb/bridges.py           |  33 ++++-
 bridgedb/main.py              | 297 ++++--------------------------------------
 bridgedb/rdsys.py             | 100 ++++++++++++++
 bridgedb/test/test_Storage.py |  55 --------
 bridgedb/test/test_main.py    | 145 +++------------------
 6 files changed, 175 insertions(+), 461 deletions(-)

diff --git a/bridgedb.conf b/bridgedb.conf
index 6d4839d..9e1374a 100644
--- a/bridgedb.conf
+++ b/bridgedb.conf
@@ -150,6 +150,12 @@ STATUS_FILE = "networkstatus-bridges"
 #
 IGNORE_NETWORKSTATUS = True
 
+# The token to access the rdsys backend
+RDSYS_TOKEN = "ApiTokenPlaceholder"
+
+# The address of the rdsys backend
+RDSYS_ADDRESS = "localhost:7100"
+
 #----------------
 # Output Files   \  Where to store created data
 #------------------------------------------------------------------------------
diff --git a/bridgedb/bridges.py b/bridgedb/bridges.py
index 6ec864d..83d6e95 100644
--- a/bridgedb/bridges.py
+++ b/bridgedb/bridges.py
@@ -34,7 +34,6 @@ import codecs
 import hashlib
 import ipaddr
 import logging
-import os
 import warnings
 
 from Crypto.Util import asn1
@@ -52,14 +51,12 @@ from bridgedb import safelog
 from bridgedb import bridgerequest
 from bridgedb.crypto import removePKCS1Padding
 from bridgedb.parse.addr import isIPAddress
-from bridgedb.parse.addr import isIPv4
 from bridgedb.parse.addr import isIPv6
 from bridgedb.parse.addr import isValidIP
 from bridgedb.parse.addr import PortList
 from bridgedb.parse.fingerprint import isValidFingerprint
 from bridgedb.parse.fingerprint import toHex
 from bridgedb.parse.fingerprint import fromHex
-from bridgedb.parse.nickname import isValidRouterNickname
 from bridgedb.util import isascii_noncontrol
 
 
@@ -1549,6 +1546,36 @@ class Bridge(BridgeBackwardsCompatibility):
         """
         return list(set([pt.methodname for pt in self.transports]))
 
+    def updateFromResource(self, resource):
+        """Update this bridge's attributes from an rdsys resource
+
+        :type resource: dict
+        :param resource: The rdsys resource dict
+        """
+        self.fingerprint = resource["fingerprint"]
+        self.address = resource["address"]
+        self.orPort = resource["port"]
+
+        self.flags.running = resource["flags"]["running"]
+        self.flags.stable = resource["flags"]["stable"]
+        self.flags.valid = resource["flags"]["valid"]
+        self.flags.fast = resource["flags"]["fast"]
+
+        if resource["or-addresses"]:
+            for oa in resource["or-addresses"]:
+                validatedAddress = isIPAddress(oa["address"], compressed=False)
+                if validatedAddress:
+                    self.orAddresses.append( (validatedAddress, oa["port"], oa["ip-version"],) )
+
+        transport = PluggableTransport(
+                fingerprint=self.fingerprint,
+                methodname=resource["type"],
+                address=self.address,
+                port=self.port,
+                arguments=resource.get("params", {})
+                )
+        self.transports = [transport]
+
     def updateFromNetworkStatus(self, descriptor, ignoreNetworkstatus=False):
         """Update this bridge's attributes from a parsed networkstatus
         document.
diff --git a/bridgedb/main.py b/bridgedb/main.py
index 8fdec23..72c1f0e 100644
--- a/bridgedb/main.py
+++ b/bridgedb/main.py
@@ -27,6 +27,7 @@ from bridgedb import runner
 from bridgedb import util
 from bridgedb import metrics
 from bridgedb import antibot
+from bridgedb import rdsys
 from bridgedb.bridges import MalformedBridgeInfo
 from bridgedb.bridges import MissingServerDescriptorDigest
 from bridgedb.bridges import ServerDescriptorDigestMismatch
@@ -57,24 +58,6 @@ def expandBridgeAuthDir(authdir, filename):
 
     return path
 
-def writeAssignments(hashring, filename):
-    """Dump bridge distributor assignments to disk.
-
-    :type hashring: A :class:`~bridgedb.bridgerings.BridgeSplitter`
-    :ivar hashring: A class which takes an HMAC key and splits bridges
-        into their hashring assignments.
-    :param str filename: The filename to write the assignments to.
-    """
-    logging.debug("Dumping pool assignments to file: '%s'" % filename)
-
-    try:
-        with open(filename, 'a') as fh:
-            fh.write("bridge-pool-assignment %s\n" %
-                     time.strftime("%Y-%m-%d %H:%M:%S"))
-            hashring.dumpAssignments(fh)
-    except IOError:
-        logging.info("I/O error while writing assignments to: '%s'" % filename)
-
 def writeMetrics(filename, measurementInterval):
     """Dump usage metrics to disk.
 
@@ -91,178 +74,18 @@ def writeMetrics(filename, measurementInterval):
     except IOError as err:
         logging.error("Failed to write metrics to '%s': %s" % (filename, err))
 
-def load(state, hashring, clear=False):
-    """Read and parse all descriptors, and load into a bridge hashring.
-
-    Read all the appropriate bridge files from the saved
-    :class:`~bridgedb.persistent.State`, parse and validate them, and then
-    store them into our ``state.hashring`` instance. The ``state`` will be
-    saved again at the end of this function.
+def load(cfg, proxyList, key):
+    """Load the configured distributors and their connections to rdsys
 
-    :type hashring: :class:`~bridgedb.bridgerings.BridgeSplitter`
-    :param hashring: A class which provides a mechanism for HMACing
-        Bridges in order to assign them to hashrings.
-    :param boolean clear: If True, clear all previous bridges from the
-        hashring before parsing for new ones.
-    """
-    if not state:
-        logging.fatal("bridgedb.main.load() could not retrieve state!")
-        sys.exit(2)
-
-    if clear:
-        logging.info("Clearing old bridges...")
-        hashring.clear()
-
-    logging.info("Loading bridges...")
-
-    ignoreNetworkstatus = state.IGNORE_NETWORKSTATUS
-    if ignoreNetworkstatus:
-        logging.info("Ignoring BridgeAuthority networkstatus documents.")
-
-    for auth in state.BRIDGE_AUTHORITY_DIRECTORIES:
-        logging.info("Processing descriptors in %s directory..." % auth)
-
-        bridges = {}
-        timestamps = {}
-
-        fn = expandBridgeAuthDir(auth, state.STATUS_FILE)
-        logging.info("Opening networkstatus file: %s" % fn)
-        networkstatuses = descriptors.parseNetworkStatusFile(fn)
-        logging.debug("Closing networkstatus file: %s" % fn)
-
-        logging.info("Processing networkstatus descriptors...")
-        for router in networkstatuses:
-            bridge = Bridge()
-            bridge.updateFromNetworkStatus(router, ignoreNetworkstatus)
-            try:
-                bridge.assertOK()
-            except MalformedBridgeInfo as error:
-                logging.warn(str(error))
-            else:
-                bridges[bridge.fingerprint] = bridge
-
-        for filename in state.BRIDGE_FILES:
-            fn = expandBridgeAuthDir(auth, filename)
-            logging.info("Opening bridge-server-descriptor file: '%s'" % fn)
-            serverdescriptors = descriptors.parseServerDescriptorsFile(fn)
-            logging.debug("Closing bridge-server-descriptor file: '%s'" % fn)
-
-            for router in serverdescriptors:
-                try:
-                    bridge = bridges[router.fingerprint]
-                except KeyError:
-                    logging.warn(
-                        ("Received server descriptor for bridge '%s' which wasn't "
-                         "in the networkstatus!") % router.fingerprint)
-                    if ignoreNetworkstatus:
-                        bridge = Bridge()
-                    else:
-                        continue
-
-                try:
-                    bridge.updateFromServerDescriptor(router, ignoreNetworkstatus)
-                except (ServerDescriptorWithoutNetworkstatus,
-                        MissingServerDescriptorDigest,
-                        ServerDescriptorDigestMismatch) as error:
-                    logging.warn(str(error))
-                    # Reject any routers whose server descriptors didn't pass
-                    # :meth:`~bridges.Bridge._checkServerDescriptor`, i.e. those
-                    # bridges who don't have corresponding networkstatus
-                    # documents, or whose server descriptor digests don't check
-                    # out:
-                    bridges.pop(router.fingerprint)
-                    continue
-
-                if state.COLLECT_TIMESTAMPS:
-                    # Update timestamps from server descriptors, not from network
-                    # status descriptors (because networkstatus documents and
-                    # descriptors aren't authenticated in any way):
-                    if bridge.fingerprint in timestamps.keys():
-                        timestamps[bridge.fingerprint].append(router.published)
-                    else:
-                        timestamps[bridge.fingerprint] = [router.published]
-
-        eifiles = [expandBridgeAuthDir(auth, fn) for fn in state.EXTRA_INFO_FILES]
-        extrainfos = descriptors.parseExtraInfoFiles(*eifiles)
-        for fingerprint, router in extrainfos.items():
-            try:
-                bridges[fingerprint].updateFromExtraInfoDescriptor(router)
-            except MalformedBridgeInfo as error:
-                logging.warn(str(error))
-            except KeyError as error:
-                logging.warn(("Received extrainfo descriptor for bridge '%s', "
-                              "but could not find bridge with that fingerprint.")
-                             % router.fingerprint)
-
-        blacklist = parseBridgeBlacklistFile(state.NO_DISTRIBUTION_FILE)
-
-        inserted = 0
-        logging.info("Trying to insert %d bridges into hashring, %d of which "
-                     "have the 'Running' flag..." % (len(bridges),
-                     len(list(filter(lambda b: b.flags.running, bridges.values())))))
-
-        for fingerprint, bridge in bridges.items():
-            # Skip insertion of bridges which are geolocated to be in one of the
-            # NO_DISTRIBUTION_COUNTRIES, a.k.a. the countries we don't distribute
-            # bridges from:
-            if bridge.country in state.NO_DISTRIBUTION_COUNTRIES:
-                logging.warn("Not distributing Bridge %s %s:%s in country %s!" %
-                             (bridge, bridge.address, bridge.orPort, bridge.country))
-            # Skip insertion of blacklisted bridges.
-            elif bridge in blacklist.keys():
-                logging.warn("Not distributing blacklisted Bridge %s %s:%s: %s" %
-                             (bridge, bridge.address, bridge.orPort, blacklist[bridge]))
-            # Skip bridges that are running a blacklisted version of Tor.
-            elif bridge.runsVersion(state.BLACKLISTED_TOR_VERSIONS):
-                logging.warn("Not distributing bridge %s because it runs blacklisted "
-                             "Tor version %s." % (router.fingerprint, bridge.software))
-            else:
-                # If the bridge is not running, then it is skipped during the
-                # insertion process.
-                hashring.insert(bridge)
-                inserted += 1
-        logging.info("Tried to insert %d bridges into hashring.  Resulting "
-                     "hashring is of length %d." % (inserted, len(hashring)))
-
-        if state.COLLECT_TIMESTAMPS:
-            reactor.callInThread(updateBridgeHistory, bridges, timestamps)
-
-        state.save()
-
-def _reloadFn(*args):
-    """Placeholder callback function for :func:`_handleSIGHUP`."""
-    return True
-
-def _handleSIGHUP(*args):
-    """Called when we receive a SIGHUP; invokes _reloadFn."""
-    reactor.callInThread(_reloadFn)
-
-def replaceBridgeRings(current, replacement):
-    """Replace the current thing with the new one"""
-    current.hashring = replacement.hashring
-
-def createBridgeRings(cfg, proxyList, key):
-    """Create the bridge distributors defined by the config file
-
-    :type cfg:  :class:`Conf`
-    :param cfg: The current configuration, including any in-memory settings
-        (i.e. settings whose values were not obtained from the config file,
-        but were set via a function somewhere)
+    :type cfg: :class:`Conf`
+    :ivar cfg: The current configuration, including any in-memory
+        settings (i.e. settings whose values were not obtained from the
+        config file, but were set via a function somewhere)
     :type proxyList: :class:`~bridgedb.proxy.ProxySet`
     :param proxyList: The container for the IP addresses of any currently
         known open proxies.
     :param bytes key: Hashring master key
-    :rtype: tuple
-    :returns: A :class:`~bridgedb.bridgerings.BridgeSplitter` hashring, an
-        :class:`~bridgedb.distributors.https.distributor.HTTPSDistributor` or None, and an
-        :class:`~bridgedb.distributors.email.distributor.EmailDistributor` or None, and an
-        :class:`~bridgedb.distributors.moat.distributor.MoatDistributor` or None.
     """
-    # Create a BridgeSplitter to assign the bridges to the different
-    # distributors.
-    hashring = bridgerings.BridgeSplitter(crypto.getHMAC(key, "Hashring-Key"))
-    logging.debug("Created hashring: %r" % hashring)
-
     # Create ring parameters.
     ringParams = bridgerings.BridgeRingParameters(needPorts=cfg.FORCE_PORTS,
                                                   needFlags=cfg.FORCE_FLAGS)
@@ -277,7 +100,8 @@ def createBridgeRings(cfg, proxyList, key):
             crypto.getHMAC(key, "Moat-Dist-Key"),
             proxyList,
             answerParameters=ringParams)
-        hashring.addRing(moatDistributor.hashring, "moat", cfg.MOAT_SHARE)
+        moatDistributor.prepopulateRings()
+        rdsys.start_stream("moat", cfg.RDSYS_TOKEN, cfg.RDSYS_ADDRESS, moatDistributor.hashring)
 
     # As appropriate, create an IP-based distributor.
     if cfg.HTTPS_DIST and cfg.HTTPS_SHARE:
@@ -287,7 +111,8 @@ def createBridgeRings(cfg, proxyList, key):
             crypto.getHMAC(key, "HTTPS-IP-Dist-Key"),
             proxyList,
             answerParameters=ringParams)
-        hashring.addRing(ipDistributor.hashring, "https", cfg.HTTPS_SHARE)
+        ipDistributor.prepopulateRings()
+        rdsys.start_stream("https", cfg.RDSYS_TOKEN, cfg.RDSYS_ADDRESS, ipDistributor.hashring)
 
     # As appropriate, create an email-based distributor.
     if cfg.EMAIL_DIST and cfg.EMAIL_SHARE:
@@ -298,40 +123,18 @@ def createBridgeRings(cfg, proxyList, key):
             cfg.EMAIL_DOMAIN_RULES.copy(),
             answerParameters=ringParams,
             whitelist=cfg.EMAIL_WHITELIST.copy())
-        hashring.addRing(emailDistributor.hashring, "email", cfg.EMAIL_SHARE)
-
-    # As appropriate, tell the hashring to leave some bridges unallocated.
-    if cfg.RESERVED_SHARE:
-        hashring.addRing(bridgerings.UnallocatedHolder(),
-                         "unallocated",
-                         cfg.RESERVED_SHARE)
+        emailDistributor.prepopulateRings()
+        rdsys.start_stream("email", cfg.RDSYS_TOKEN, cfg.RDSYS_ADDRESS, emailDistributor.hashring)
 
-    return hashring, emailDistributor, ipDistributor, moatDistributor
+    return emailDistributor, ipDistributor, moatDistributor
 
-def loadBlockedBridges(hashring):
-    """Load bridge blocking info from our SQL database and add it to bridge
-    objects."""
-
-    blockedBridges = {}
-    with bridgedb.Storage.getDB() as db:
-        blockedBridges = db.getBlockedBridges()
-
-    num_blocked = 0
-    for name, ring in hashring.ringsByName.items():
-        if name == "unallocated":
-            continue
-        for _, bridge in ring.bridges.items():
-            l = []
-            try:
-                l = blockedBridges[bridge.fingerprint]
-            except KeyError:
-                continue
-            for blocking_country, address, port in l:
-                bridge.setBlockedIn(blocking_country, address, port)
-            num_blocked += 1
-
-    logging.info("Loaded blocking info for %d bridges.".format(num_blocked))
+def _reloadFn(*args):
+    """Placeholder callback function for :func:`_handleSIGHUP`."""
+    return True
 
+def _handleSIGHUP(*args):
+    """Called when we receive a SIGHUP; invokes _reloadFn."""
+    reactor.callInThread(_reloadFn)
 
 def run(options, reactor=reactor):
     """This is BridgeDB's main entry point and main runtime loop.
@@ -416,7 +219,7 @@ def run(options, reactor=reactor):
         State should be saved before calling this method, and will be saved
         again at the end of it.
 
-        The internal variables ``cfg`` and ``hashring`` are taken from a
+        The internal variables ``cfg`` is taken from a
         :class:`~bridgedb.persistent.State` instance, which has been saved to a
         statefile with :meth:`bridgedb.persistent.State.save`.
 
@@ -424,9 +227,6 @@ def run(options, reactor=reactor):
         :ivar cfg: The current configuration, including any in-memory
             settings (i.e. settings whose values were not obtained from the
             config file, but were set via a function somewhere)
-        :type hashring: A :class:`~bridgedb.bridgerings.BridgeSplitter`
-        :ivar hashring: A class which takes an HMAC key and splits bridges
-            into their hashring assignments.
         """
         logging.debug("Caught SIGHUP")
         logging.info("Reloading...")
@@ -455,71 +255,20 @@ def run(options, reactor=reactor):
         logging.info("Reloading decoy bridges...")
         antibot.loadDecoyBridges(config.DECOY_BRIDGES_FILE)
 
-        (hashring,
-         emailDistributorTmp,
-         ipDistributorTmp,
-         moatDistributorTmp) = createBridgeRings(cfg, proxies, key)
-
         # Initialize our DB.
         bridgedb.Storage.initializeDBLock()
         bridgedb.Storage.setDBFilename(cfg.DB_FILE + ".sqlite")
-        logging.info("Reparsing bridge descriptors...")
-        load(state, hashring, clear=False)
-        logging.info("Bridges loaded: %d" % len(hashring))
-        loadBlockedBridges(hashring)
 
-        if emailDistributorTmp is not None:
-            emailDistributorTmp.prepopulateRings() # create default rings
-        else:
-            logging.warn("No email distributor created!")
-
-        if ipDistributorTmp is not None:
-            ipDistributorTmp.prepopulateRings() # create default rings
-        else:
-            logging.warn("No HTTP(S) distributor created!")
-
-        if moatDistributorTmp is not None:
-            moatDistributorTmp.prepopulateRings()
-        else:
-            logging.warn("No Moat distributor created!")
-
-        metrix = metrics.InternalMetrics()
-        logging.info("Logging bridge ring metrics for %d rings." %
-                     len(hashring.ringsByName))
-        for ringName, ring in hashring.ringsByName.items():
-            # Ring is of type FilteredBridgeSplitter or UnallocatedHolder.
-            # FilteredBridgeSplitter splits bridges into subhashrings based on
-            # filters.
-            if hasattr(ring, "filterRings"):
-                for (ringname, (filterFn, subring)) in ring.filterRings.items():
-                    subRingName = "-".join(ring.extractFilterNames(ringname))
-                    metrix.recordBridgesInHashring(ringName,
-                                                   subRingName,
-                                                   len(subring))
-            elif hasattr(ring, "fingerprints"):
-                metrix.recordBridgesInHashring(ringName, "unallocated",
-                                               len(ring.fingerprints))
-
-        # Dump bridge pool assignments to disk.
-        writeAssignments(hashring, state.ASSIGNMENTS_FILE)
         state.save()
 
         if inThread:
             # XXX shutdown the distributors if they were previously running
             # and should now be disabled
-            if moatDistributorTmp:
-                reactor.callFromThread(replaceBridgeRings,
-                                       moatDistributor, moatDistributorTmp)
-            if ipDistributorTmp:
-                reactor.callFromThread(replaceBridgeRings,
-                                       ipDistributor, ipDistributorTmp)
-            if emailDistributorTmp:
-                reactor.callFromThread(replaceBridgeRings,
-                                       emailDistributor, emailDistributorTmp)
+            pass
         else:
             # We're still starting up. Return these distributors so
             # they are configured in the outer-namespace
-            return emailDistributorTmp, ipDistributorTmp, moatDistributorTmp
+            return load(cfg, proxies, key)
 
     global _reloadFn
     _reloadFn = reload
diff --git a/bridgedb/rdsys.py b/bridgedb/rdsys.py
new file mode 100644
index 0000000..15c5ce2
--- /dev/null
+++ b/bridgedb/rdsys.py
@@ -0,0 +1,100 @@
+import json
+import secrets
+import logging
+from io import BytesIO
+from twisted.internet import reactor
+from twisted.internet.defer import Deferred
+from twisted.internet.protocol import Protocol
+from twisted.web.client import Agent, FileBodyProducer
+from twisted.web.http_headers import Headers
+
+from bridgedb.bridges import Bridge
+
+
+inter_message_delimiter = b"\r"
+
+
+class RdsysProtocol(Protocol):
+    def __init__(self, finished, hashring):
+        """
+        :type hashring: :class:`bridgedb.bridgerings.FilteredBridgeSplitter`
+        """
+        self.finished = finished
+        self.hashring = hashring
+        self.buff = b""
+
+    def dataReceived(self, data):
+        """
+        dataReceived is being called by twisted web client for each chunk it
+        does receives. One chunk might not be full message from rdsys but a
+        part of it, or the end of one and the beginning of the next, or
+        multiple messages. We don't expect to be multiple messages in one
+        chunk with rdsys, but anyway is implemented to support that usecase.
+
+        self.buff is the accumulator, where we aggregate the chunks and when
+        we find a inter_message_delimiter we update resources and reset
+        self.buff setting it to the first part of the next message if there
+        is one if not the data.split will anyway produce an empty bytes.
+        """
+        parts = data.split(inter_message_delimiter)
+        self.buff += parts[0]
+        for part in parts[1:]:
+            self._updateResources()
+            self.buff = part
+
+    def _updateResources(self):
+        jb = json.loads(self.buff)
+        for action, fn in [
+            ("gone", self.hashring.remove),
+            ("changed", self.hashring.insert),
+            ("new", self.hashring.insert),
+        ]:
+            if jb[action] is None:
+                continue
+
+            for rtype in jb[action]:
+                if jb[action][rtype] is None:
+                    continue
+
+                for resource in jb[action][rtype]:
+                    bridge = Bridge()
+                    bridge.updateFromResource(resource)
+                    fn(bridge)
+
+    def connectionLost(self, reason):
+        logging.info("Connection lost with rdsys backend:", reason.getErrorMessage())
+        self.finished.callback(None)
+
+
+def start_stream(distributor, token, rdsys_address, hashring):
+    headers = Headers(
+        {
+            "Content-Type": ["application/json"],
+            "Authorization": ["Bearer %s" % (token,)],
+        }
+    )
+    body = {
+        "request_origin": distributor,
+        "resource_types": ["obfs4", "vanilla"],
+    }
+    buff = BytesIO(bytes(json.dumps(body), "utf-8"))
+    body_producer = FileBodyProducer(buff)
+    agent = Agent(reactor)
+
+    def cbResponse(r):
+        finished = Deferred()
+        r.deliverBody(RdsysProtocol(finished, hashring))
+        return finished
+
+    def connect():
+        d = agent.request(
+            b"GET",
+            b"http://%s/resource-stream" % (rdsys_address.encode(),),
+            headers=headers,
+            bodyProducer=body_producer,
+        )
+        d.addCallback(cbResponse)
+        d.addErrback(lambda err: logging.warning("Error on the connection with rdsys: " + str(err)))
+        d.addCallback(connect)
+
+    connect()
diff --git a/bridgedb/test/test_Storage.py b/bridgedb/test/test_Storage.py
index a1eb7bd..c641058 100644
--- a/bridgedb/test/test_Storage.py
+++ b/bridgedb/test/test_Storage.py
@@ -155,61 +155,6 @@ class DatabaseTest(unittest.TestCase):
             # Measurements that are "young enough" should be returned.
             self.assertEquals(len(rows), 1)
 
-    def test_main_loadBlockedBridges(self):
-        Storage.initializeDBLock()
-
-        # Mock configuration object that we use to initialize our bridge rings.
-        class Cfg(object):
-            def __init__(self):
-                self.FORCE_PORTS = [(443, 1)]
-                self.FORCE_FLAGS = [("Stable", 1)]
-                self.MOAT_DIST = False
-                self.HTTPS_DIST = True
-                self.HTTPS_SHARE = 10
-                self.N_IP_CLUSTERS = 1
-                self.EMAIL_DIST = False
-                self.RESERVED_SHARE = 0
-
-        bridge = self.fakeBridges[0]
-        addr, port, _ = bridge.orAddresses[0]
-        cc= "de"
-
-        # Mock object that we use to simulate a database connection.
-        class DummyDB(object):
-            def __init__(self):
-                pass
-            def __enter__(self):
-                return self
-            def __exit__(self, type, value, traceback):
-                pass
-            def getBlockedBridges(self):
-                return {bridge.fingerprint: [(cc, addr, port)]}
-            def getBridgeDistributor(self, bridge, validRings):
-                return "https"
-            def insertBridgeAndGetRing(self, bridge, setRing, seenAt, validRings, defaultPool="unallocated"):
-                return "https"
-            def commit(self):
-                pass
-
-        oldObj = Storage.getDB
-        Storage.getDB = DummyDB
-
-        hashring, _, _, _ = main.createBridgeRings(Cfg(), None, b'key')
-        hashring.insert(bridge)
-
-        self.assertEqual(len(hashring), 1)
-        self.assertFalse(bridge.isBlockedIn(cc))
-        self.assertFalse(bridge.isBlockedIn("ab"))
-        self.assertFalse(bridge.addressIsBlockedIn(cc, addr, port))
-
-        main.loadBlockedBridges(hashring)
-
-        self.assertTrue(bridge.isBlockedIn(cc))
-        self.assertFalse(bridge.isBlockedIn("ab"))
-        self.assertTrue(bridge.addressIsBlockedIn(cc, addr, port))
-
-        Storage.getDB = oldObj
-
     def test_getBlockedBridgesFromSql(self):
 
         elems = [(0, "0000000000000000000000000000000000000000", "obfs4",
diff --git a/bridgedb/test/test_main.py b/bridgedb/test/test_main.py
index 64b65a4..ae6ed9a 100644
--- a/bridgedb/test/test_main.py
+++ b/bridgedb/test/test_main.py
@@ -132,11 +132,6 @@ class BridgedbTests(unittest.TestCase):
 
         return updatedPaths
 
-    def _cbAssertFingerprints(self, d):
-        """Assert that there are some bridges in the hashring."""
-        self.assertGreater(len(self.hashring), 0)
-        return d
-
     def _cbCallUpdateBridgeHistory(self, d, hashring):
         """Fake some timestamps for the bridges in the hashring, and then call
         main.updateBridgeHistory().
@@ -220,73 +215,6 @@ class BridgedbTests(unittest.TestCase):
         for d in self._directories_created:
             shutil.rmtree(d)
 
-    def test_main_updateBridgeHistory(self):
-        """main.updateBridgeHistory should update some timestamps for some
-        bridges.
-        """
-        # Mock the updateBridgeHistory() function so that we don't try to
-        # access the database:
-        main.updateBridgeHistory = mockUpdateBridgeHistory
-
-        # Get the bridges into the mocked hashring
-        d = deferToThread(main.load, self.state, self.hashring)
-        d.addCallback(self._cbAssertFingerprints)
-        d.addErrback(self._eb_Failure)
-        d.addCallback(self._cbCallUpdateBridgeHistory, self.hashring)
-        d.addErrback(self._eb_Failure)
-        return d
-
-    def test_main_load(self):
-        """main.load() should run without error."""
-        d = deferToThread(main.load, self.state, self.hashring)
-        d.addCallback(self._cbAssertFingerprints)
-        d.addErrback(self._eb_Failure)
-        return d
-
-    def test_main_load_then_reload(self):
-        """main.load() should run without error."""
-        d = deferToThread(main.load, self.state, self.hashring)
-        d.addCallback(self._cbAssertFingerprints)
-        d.addErrback(self._eb_Failure)
-        d.addCallback(main._reloadFn)
-        d.addErrback(self._eb_Failure)
-        return d
-
-    def test_main_load_no_state(self):
-        """main.load() should raise SystemExit without a state object."""
-        self.assertRaises(SystemExit, main.load, None, self.hashring)
-
-    def test_main_load_clear(self):
-        """When called with clear=True, load() should run and clear the
-        hashrings.
-        """
-        d = deferToThread(main.load, self.state, self.hashring, clear=True)
-        d.addCallback(self._cbAssertFingerprints)
-        d.addErrback(self._eb_Failure)
-        return d
-
-    def test_main_load_collect_timestamps(self):
-        """When COLLECT_TIMESTAMPS=True, main.load() should call
-        main.updateBridgeHistory().
-        """
-        # Mock the addOrUpdateBridgeHistory() function so that we don't try to
-        # access the database:
-        main.updateBridgeHistory = mockUpdateBridgeHistory
-        state = self.state
-        state.COLLECT_TIMESTAMPS = True
-
-        # The reactor is deferring this to a thread, so the test execution
-        # here isn't actually covering the Storage.updateBridgeHistory()
-        # function:
-        main.load(state, self.hashring)
-
-    def test_main_load_malformed_networkstatus(self):
-        """When called with a networkstatus file with an invalid descriptor,
-        main.load() should raise a ValueError.
-        """
-        self._appendToFile(self.state.STATUS_FILE, NETWORKSTATUS_MALFORMED)
-        self.assertRaises(ValueError, main.load, self.state, self.hashring)
-
     def test_main_reloadFn(self):
         """main._reloadFn() should return True."""
         self.assertTrue(main._reloadFn())
@@ -297,80 +225,39 @@ class BridgedbTests(unittest.TestCase):
 
         self.assertTrue(main._handleSIGHUP())
 
-    def test_main_createBridgeRings(self):
-        """main.createBridgeRings() should add three hashrings to the
-        hashring.
-        """
-        proxyList = None
-        (hashring, emailDist, httpsDist, moatDist) = main.createBridgeRings(
-            self.config, proxyList, self.key)
-
-        # Should have an HTTPSDistributor ring, an EmailDistributor ring,
-        # a MoatDistributor right, and an UnallocatedHolder ring:
-        self.assertEqual(len(hashring.ringsByName.keys()), 4)
-
-    def test_main_createBridgeRings_with_proxyList(self):
-        """main.createBridgeRings() should add three hashrings to the
-        hashring and add the proxyList to the IPBasedDistibutor.
+    def test_main_load_with_proxyList(self):
+        """main.load() should add the proxyList to the IPBasedDistibutor.
         """
         exitRelays = ['1.1.1.1', '2.2.2.2', '3.3.3.3']
         proxyList = main.proxy.ProxySet()
         proxyList.addExitRelays(exitRelays)
-        (hashring, emailDist, httpsDist, moatDist) = main.createBridgeRings(
+        (emailDist, httpsDist, moatDist) = main.load(
             self.config, proxyList, self.key)
 
-        # Should have an HTTPSDistributor ring, an EmailDistributor ring,
-        # a MoatDistributor ring, and an UnallocatedHolder ring:
-        self.assertEqual(len(hashring.ringsByName.keys()), 4)
         self.assertGreater(len(httpsDist.proxies), 0)
         self.assertCountEqual(exitRelays, httpsDist.proxies)
 
-    def test_main_createBridgeRings_no_https_dist(self):
-        """When HTTPS_DIST=False, main.createBridgeRings() should add only
-        two hashrings to the hashring.
+    def test_main_load_no_https_dist(self):
+        """When HTTPS_DIST=False, main.load() should not create an http distributor.
         """
         proxyList = main.proxy.ProxySet()
         config = self.config
         config.HTTPS_DIST = False
-        (hashring, emailDist, httpsDist, moatDist) = main.createBridgeRings(
-            config, proxyList, self.key)
-
-        # Should have an EmailDistributor ring, a MoatDistributor ring, and an
-        # UnallocatedHolder ring:
-        self.assertEqual(len(hashring.ringsByName.keys()), 3)
-        self.assertNotIn('https', hashring.rings)
-        self.assertNotIn(httpsDist, hashring.ringsByName.values())
-
-    def test_main_createBridgeRings_no_email_dist(self):
-        """When EMAIL_DIST=False, main.createBridgeRings() should add only
-        two hashrings to the hashring.
+        (emailDist, httpsDist, moatDist) = main.load(
+            self.config, proxyList, self.key)
+
+        self.assertEqual(httpsDist, None)
+
+    def test_main_load_no_email_dist(self):
+        """When EMAIL_DIST=False, main.load() should not create an email distributor.
         """
         proxyList = main.proxy.ProxySet()
         config = self.config
         config.EMAIL_DIST = False
-        (hashring, emailDist, httpsDist, moatDist) = main.createBridgeRings(
-            config, proxyList, self.key)
-
-        # Should have an HTTPSDistributor ring, a MoatDistributor ring, and an
-        # UnallocatedHolder ring:
-        self.assertEqual(len(hashring.ringsByName.keys()), 3)
-        self.assertNotIn('email', hashring.rings)
-        self.assertNotIn(emailDist, hashring.ringsByName.values())
-
-    def test_main_createBridgeRings_no_reserved_share(self):
-        """When RESERVED_SHARE=0, main.createBridgeRings() should add only
-        two hashrings to the hashring.
-        """
-        proxyList = main.proxy.ProxySet()
-        config = self.config
-        config.RESERVED_SHARE = 0
-        (hashring, emailDist, httpsDist, moatDist) = main.createBridgeRings(
-            config, proxyList, self.key)
-
-        # Should have an HTTPSDistributor ring, an EmailDistributor ring, and a
-        # MoatDistributor ring:
-        self.assertEqual(len(hashring.ringsByName.keys()), 3)
-        self.assertNotIn('unallocated', hashring.rings)
+        (emailDist, httpsDist, moatDist) = main.load(
+            self.config, proxyList, self.key)
+
+        self.assertEqual(emailDist, None)
 
     def test_main_run(self):
         """main.run() should run and then finally raise SystemExit."""





More information about the tor-commits mailing list