From 3b6e1e344bca71cf8e18bcee996b141e99ebce3c Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Thu, 27 Jun 2019 13:15:10 -0400 Subject: [PATCH] Don't blow up the web status if we get an unrecognized announcement --- src/allmydata/test/test_storage_client.py | 25 +++++++++++++++++++++++ src/allmydata/util/connection_status.py | 22 +++++++++++++++++--- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/allmydata/test/test_storage_client.py b/src/allmydata/test/test_storage_client.py index 321eda2cd..fbd4fa199 100644 --- a/src/allmydata/test/test_storage_client.py +++ b/src/allmydata/test/test_storage_client.py @@ -8,9 +8,16 @@ from twisted.application.service import ( from twisted.trial import unittest from twisted.internet.defer import succeed, inlineCallbacks +from foolscap.api import ( + Tub, +) + from allmydata.util import base32, yamlutil from allmydata.storage_client import NativeStorageServer from allmydata.storage_client import StorageFarmBroker +from allmydata.interfaces import ( + IConnectionStatus, +) class NativeStorageServerWithVersion(NativeStorageServer): @@ -48,6 +55,24 @@ class TestNativeStorageServer(unittest.TestCase): self.assertEqual(nss.get_nickname(), "") +class GetConnectionStatus(unittest.TestCase): + """ + Tests for ``NativeStorageServer.get_connection_status``. + """ + def test_unrecognized_announcement(self): + """ + When ``NativeStorageServer`` is constructed with a storage announcement it + doesn't recognize, its ``get_connection_status`` nevertheless returns + an object which provides ``IConnectionStatus``. + """ + # Pretty hard to recognize anything from an empty announcement. + ann = {} + nss = NativeStorageServer("server_id", ann, Tub, {}) + nss.start_connecting(lambda: None) + connection_status = nss.get_connection_status() + self.assertTrue(IConnectionStatus.providedBy(connection_status)) + + class UnrecognizedAnnouncement(unittest.TestCase): """ Tests for handling of announcements that aren't recognized and don't use diff --git a/src/allmydata/util/connection_status.py b/src/allmydata/util/connection_status.py index 3f5dd5278..44c12f220 100644 --- a/src/allmydata/util/connection_status.py +++ b/src/allmydata/util/connection_status.py @@ -12,6 +12,20 @@ class ConnectionStatus(object): self.last_connection_time = last_connection_time self.last_received_time = last_received_time + @classmethod + def unstarted(cls): + """ + Create a ``ConnectionStatus`` representing a connection for which no + attempts have yet been made. + """ + return cls( + connected=False, + summary=u"unstarted", + non_connected_statuses=[], + last_connection_time=None, + last_received_time=None, + ) + def _hint_statuses(which, handlers, statuses): non_connected_statuses = {} for hint in which: @@ -23,10 +37,12 @@ def _hint_statuses(which, handlers, statuses): def from_foolscap_reconnector(rc, last_received): ri = rc.getReconnectionInfo() + # See foolscap/reconnector.py, ReconnectionInfo, for details about + # possible states. state = ri.state - # the Reconnector shouldn't even be exposed until it is started, so we - # should never see "unstarted" - assert state in ("connected", "connecting", "waiting"), state + if state == "unstarted": + return ConnectionStatus.unstarted() + ci = ri.connectionInfo connected = False last_connected = None