commit b514fafccaa6d42d823f9b528d7335b85422c1cc Author: Damian Johnson atagar@torproject.org Date: Thu Jan 11 09:55:22 2018 -0800
Test fallback persistence
Adding a unit test to test persistance and reloading fallbacks. As expected, presently doesn't have the new fields. --- cache_fallback_directories.py | 18 +-------------- stem/descriptor/remote.py | 37 ++++++++++++++++++++++++++++-- stem/util/conf.py | 7 ++++++ test/unit/descriptor/remote.py | 52 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 94 insertions(+), 20 deletions(-)
diff --git a/cache_fallback_directories.py b/cache_fallback_directories.py index 0e61cf21..c5926c8c 100755 --- a/cache_fallback_directories.py +++ b/cache_fallback_directories.py @@ -10,7 +10,6 @@ import re import sys
import stem.descriptor.remote -import stem.util.conf import stem.util.system
try: @@ -49,19 +48,4 @@ if __name__ == '__main__':
print('Differences detected...\n') print(stem.descriptor.remote._fallback_directory_differences(cached_fallback_directories, latest_fallback_directories)) - - conf = stem.util.conf.Config() - conf.set('tor_commit', fallback_dir_commit) - conf.set('stem_commit', stem_commit) - - for directory in sorted(latest_fallback_directories.values(), key = lambda x: x.fingerprint): - fingerprint = directory.fingerprint - conf.set('%s.address' % fingerprint, directory.address) - conf.set('%s.or_port' % fingerprint, str(directory.or_port)) - conf.set('%s.dir_port' % fingerprint, str(directory.dir_port)) - - if directory.orport_v6: - conf.set('%s.orport6_address' % fingerprint, str(directory.orport_v6[0])) - conf.set('%s.orport6_port' % fingerprint, str(directory.orport_v6[1])) - - conf.save(stem.descriptor.remote.CACHE_PATH) + stem.descriptor.remote.FallbackDirectory._write(latest_fallback_directories, fallback_dir_commit, stem_commit) diff --git a/stem/descriptor/remote.py b/stem/descriptor/remote.py index 8f5e5cc3..453061e0 100644 --- a/stem/descriptor/remote.py +++ b/stem/descriptor/remote.py @@ -969,7 +969,7 @@ class FallbackDirectory(Directory): self.header = header if header else {}
@staticmethod - def from_cache(): + def from_cache(path = CACHE_PATH): """ Provides fallback directory information cached with Stem. Unlike :func:`~stem.descriptor.remote.FallbackDirectory.from_remote` this doesn't @@ -977,12 +977,17 @@ class FallbackDirectory(Directory): these fallback directories are only as up to date as the Stem release we're using.
+ .. versionchanged:: 1.7.0 + Added the path argument. + + :param str path: cache file to load from + :returns: **dict** of **str** fingerprints to their :class:`~stem.descriptor.remote.FallbackDirectory` """
conf = stem.util.conf.Config() - conf.load(CACHE_PATH) + conf.load(path)
results = {}
@@ -1179,6 +1184,34 @@ class FallbackDirectory(Directory):
return section_lines
+ @staticmethod + def _write(fallbacks, tor_commit, stem_commit, path = CACHE_PATH): + """ + Persists fallback directories to a location in a way that can be read by + from_cache(). + + :param dict fallbacks: mapping of fingerprints to their fallback directory + :param str tor_commit: tor commit the fallbacks came from + :param str stem_commit: stem commit the fallbacks came from + :param str path: location fallbacks will be persisted to + """ + + conf = stem.util.conf.Config() + conf.set('tor_commit', tor_commit) + conf.set('stem_commit', stem_commit) + + for directory in sorted(fallbacks.values(), key = lambda x: x.fingerprint): + fingerprint = directory.fingerprint + conf.set('%s.address' % fingerprint, directory.address) + conf.set('%s.or_port' % fingerprint, str(directory.or_port)) + conf.set('%s.dir_port' % fingerprint, str(directory.dir_port)) + + if directory.orport_v6: + conf.set('%s.orport6_address' % fingerprint, str(directory.orport_v6[0])) + conf.set('%s.orport6_port' % fingerprint, str(directory.orport_v6[1])) + + conf.save(path) + def __hash__(self): return _hash_attr(self, 'address', 'or_port', 'dir_port', 'fingerprint', 'nickname', 'has_extrainfo', 'orport_v6', 'header', parent = Directory)
diff --git a/stem/util/conf.py b/stem/util/conf.py index d4af467d..a902cae1 100644 --- a/stem/util/conf.py +++ b/stem/util/conf.py @@ -447,6 +447,9 @@ class Config(object): # # Information for what values fail to load and why are reported to # 'stem.util.log'. + + .. versionchanged:: 1.7.0 + Class can now be used as a dictionary. """
def __init__(self): @@ -768,3 +771,7 @@ class Config(object): message_id = 'stem.util.conf.missing_config_key_%s' % key log.log_once(message_id, log.TRACE, "config entry '%s' not found, defaulting to '%s'" % (key, default)) return default + + def __getitem__(self, key): + with self._contents_lock: + return self._contents[key] diff --git a/test/unit/descriptor/remote.py b/test/unit/descriptor/remote.py index cc0d51b1..8bfdffc1 100644 --- a/test/unit/descriptor/remote.py +++ b/test/unit/descriptor/remote.py @@ -4,10 +4,12 @@ Unit tests for stem.descriptor.remote.
import io import socket +import tempfile import unittest
-import stem.prereq import stem.descriptor.remote +import stem.prereq +import stem.util.conf
try: # added in python 3.3 @@ -236,6 +238,54 @@ class TestDescriptorDownloader(unittest.TestCase):
self.assertEqual(expected, fallback_directories)
+ def test_fallback_persistence(self): + header = {'type': 'fallback', 'version': '2.0.0', 'timestamp': '20170526090242'} + + expected = { + '0756B7CD4DFC8182BE23143FAC0642F515182CEB': stem.descriptor.remote.FallbackDirectory( + address = '5.9.110.236', + or_port = 9001, + dir_port = 9030, + fingerprint = '0756B7CD4DFC8182BE23143FAC0642F515182CEB', + nickname = 'rueckgrat', + has_extrainfo = True, + orport_v6 = ('2a01:4f8:162:51e2::2', 9001), + header = header, + ), + '01A9258A46E97FF8B2CAC7910577862C14F2C524': stem.descriptor.remote.FallbackDirectory( + address = '193.171.202.146', + or_port = 9001, + dir_port = 9030, + fingerprint = '01A9258A46E97FF8B2CAC7910577862C14F2C524', + nickname = None, + has_extrainfo = False, + orport_v6 = None, + header = header, + ), + } + + excepted_config = { + 'tor_commit': ['abc'], + 'stem_commit': ['def'], + '01A9258A46E97FF8B2CAC7910577862C14F2C524.address': ['193.171.202.146'], + '01A9258A46E97FF8B2CAC7910577862C14F2C524.or_port': ['9001'], + '01A9258A46E97FF8B2CAC7910577862C14F2C524.dir_port': ['9030'], + '0756B7CD4DFC8182BE23143FAC0642F515182CEB.address': ['5.9.110.236'], + '0756B7CD4DFC8182BE23143FAC0642F515182CEB.or_port': ['9001'], + '0756B7CD4DFC8182BE23143FAC0642F515182CEB.dir_port': ['9030'], + '0756B7CD4DFC8182BE23143FAC0642F515182CEB.orport6_address': ['2a01:4f8:162:51e2::2'], + '0756B7CD4DFC8182BE23143FAC0642F515182CEB.orport6_port': ['9001'], + } + + with tempfile.NamedTemporaryFile(prefix = 'fallbacks.') as tmp: + stem.descriptor.remote.FallbackDirectory._write(expected, 'abc', 'def', tmp.name) + + conf = stem.util.conf.Config() + conf.load(tmp.name) + self.assertEqual(excepted_config, dict(conf)) + + #self.assertEqual(expected, stem.descriptor.remote.FallbackDirectory.from_cache(tmp.name)) + @patch(URL_OPEN) def test_fallback_directories_from_remote_empty(self, urlopen_mock): urlopen_mock.return_value = io.BytesIO('')