diff --git a/src/allmydata/client.py b/src/allmydata/client.py index 003a6150f..5e41e0179 100644 --- a/src/allmydata/client.py +++ b/src/allmydata/client.py @@ -106,6 +106,10 @@ the files. See the 'configuration.rst' documentation file for details. def _make_secret(): + """ + Returns a base32-encoded random secret of hashutil.CRYPTO_VAL_SIZE + bytes. + """ return base32.b2a(os.urandom(hashutil.CRYPTO_VAL_SIZE)) + "\n" @@ -258,6 +262,12 @@ def create_client_from_config(config, _client_factory=None): def _sequencer(config): + """ + :returns: a 2-tuple consisting of a new announcement + sequence-number and (random) nonce. Reads and re-writes + configuration file "announcement-seqnum", starting at 1 if that + file doesn't exist. + """ seqnum_s = config.get_config_from_file("announcement-seqnum") if not seqnum_s: seqnum_s = "0" @@ -270,6 +280,8 @@ def _sequencer(config): def create_introducer_clients(config, main_tub): """ + Read, validate and parse any 'introducers.yaml' configuration. + :returns: a list of IntroducerClient instances """ # we return this list @@ -326,8 +338,20 @@ def create_introducer_clients(config, main_tub): def create_storage_farm_broker(config, default_connection_handlers, foolscap_connection_handlers, tub_options, introducer_clients): """ - create a StorageFarmBroker object, for use by Uploader/Downloader + Create a StorageFarmBroker object, for use by Uploader/Downloader (and everybody else who wants to use storage servers) + + :param config: a _Config instance + + :param default_connection_handlers: default Foolscap handlers + + :param foolscap_connection_handlers: available/configured Foolscap + handlers + + :param dict tub_options: how to configure our Tub + + :param list introducer_clients: IntroducerClient instances if + we're connecting to any """ ps = config.get_config("client", "peers.preferred", "").split(",") preferred_peers = tuple([p.strip() for p in ps if p != ""]) diff --git a/src/allmydata/node.py b/src/allmydata/node.py index 2a511487d..9b6b8ccd5 100644 --- a/src/allmydata/node.py +++ b/src/allmydata/node.py @@ -409,6 +409,13 @@ class _Config(object): def create_tub_options(config): + """ + :param config: a _Config instance + + :returns: dict containing all Foolscap Tub-related options, + overriding defaults with appropriate config from `config` + instance. + """ # XXX this is code moved from Node -- but why are some options # camelCase and some snake_case? can we FIXME? tub_options = { @@ -430,6 +437,9 @@ def create_tub_options(config): def _make_tcp_handler(): + """ + :returns: a Foolscap default TCP handler + """ # this is always available from foolscap.connections.tcp import default return default() @@ -488,8 +498,16 @@ def create_connection_handlers(reactor, config, i2p_provider, tor_provider): def create_tub(tub_options, default_connection_handlers, foolscap_connection_handlers, handler_overrides={}, **kwargs): - # Create a Tub with the right options and handlers. It will be - # ephemeral unless the caller provides certFile= + """ + Create a Tub with the right options and handlers. It will be + ephemeral unless the caller provides certFile= in kwargs + + :param handler_overrides: anything in this will override anything + in `default_connection_handlers` for just this call. + + :param dict tub_options: every key-value pair in here will be set in + the new Tub via `Tub.setOption` + """ tub = Tub(**kwargs) for (name, value) in tub_options.items(): tub.setOption(name, value) @@ -504,6 +522,10 @@ def create_tub(tub_options, default_connection_handlers, foolscap_connection_han def _convert_tub_port(s): + """ + :returns: a proper Twisted endpoint string like (`tcp:X`) is `s` + is a bare number, or returns `s` as-is + """ if re.search(r'^\d+$', s): return "tcp:{}".format(int(s)) return s @@ -601,6 +623,26 @@ def create_main_tub(config, tub_options, default_connection_handlers, foolscap_connection_handlers, i2p_provider, tor_provider, handler_overrides={}, cert_filename="node.pem"): + """ + Creates a 'main' Foolscap Tub, typically for use as the top-level + access point for a running Node. + + :param Config: a `_Config` instance + + :param dict tub_options: any options to change in the tub + + :param default_connection_handlers: default Foolscap connection + handlers + + :param foolscap_connection_handlers: Foolscap connection + handlers for this tub + + :param i2p_provider: None, or a _Provider instance if I2P is + installed. + + :param tor_provider: None, or a _Provider instance if txtorcon + + Tor are installed. + """ portlocation = _tub_portlocation(config) certfile = config.get_private_path("node.pem") # FIXME? "node.pem" was the CERTFILE option/thing @@ -632,8 +674,11 @@ def create_main_tub(config, tub_options, def create_control_tub(): - # the control port uses a localhost-only ephemeral Tub, with no - # control over the listening port or location + """ + Creates a Foolscap Tub for use by the control port. This is a + localhost-only ephemeral Tub, with no control over the listening + port or location + """ control_tub = Tub() portnum = iputil.allocate_tcp_port() port = "tcp:%d:interface=127.0.0.1" % portnum @@ -644,7 +689,6 @@ def create_control_tub(): return control_tub - class Node(service.MultiService): """ This class implements common functionality of both Client nodes and Introducer nodes. diff --git a/src/allmydata/test/no_network.py b/src/allmydata/test/no_network.py index 922d63441..bea33e62a 100644 --- a/src/allmydata/test/no_network.py +++ b/src/allmydata/test/no_network.py @@ -187,6 +187,10 @@ class NoNetworkStorageBroker(object): # @defer.inlineCallbacks def create_no_network_client(basedir): + """ + :return: an instance of _Client subclass which does no actual + networking but has the same API. + """ basedir = abspath_expanduser_unicode(unicode(basedir)) fileutil.make_dirs(os.path.join(basedir, "private"), 0700) @@ -209,6 +213,9 @@ def create_no_network_client(basedir): class _NoNetworkClient(_Client): + """ + Overrides all _Client networking functionality to do nothing. + """ def init_connections(self): pass diff --git a/src/allmydata/test/test_client.py b/src/allmydata/test/test_client.py index fb8bc9757..84ab8af32 100644 --- a/src/allmydata/test/test_client.py +++ b/src/allmydata/test/test_client.py @@ -40,6 +40,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_comment(self): + """ + test using common comment-character in furls + """ should_fail = [r"test#test", r"#testtest", r"test\\#test"] should_not_fail = [r"test\#test", r"test\\\#test", r"testtest"] @@ -134,6 +137,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_secrets(self): + """ + A new client has renewal + cancel secrets + """ basedir = "test_client.Basic.test_secrets" os.mkdir(basedir) fileutil.write(os.path.join(basedir, "tahoe.cfg"), \ @@ -148,6 +154,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_nodekey_yes_storage(self): + """ + We have a nodeid if we're providing storage + """ basedir = "test_client.Basic.test_nodekey_yes_storage" os.mkdir(basedir) fileutil.write(os.path.join(basedir, "tahoe.cfg"), @@ -157,6 +166,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_nodekey_no_storage(self): + """ + We have a nodeid if we're not providing storage + """ basedir = "test_client.Basic.test_nodekey_no_storage" os.mkdir(basedir) fileutil.write(os.path.join(basedir, "tahoe.cfg"), @@ -166,6 +178,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_reserved_1(self): + """ + reserved_space option is propagated + """ basedir = "client.Basic.test_reserved_1" os.mkdir(basedir) fileutil.write(os.path.join(basedir, "tahoe.cfg"), \ @@ -178,6 +193,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_reserved_2(self): + """ + reserved_space option understands 'K' to mean kilobytes + """ basedir = "client.Basic.test_reserved_2" os.mkdir(basedir) fileutil.write(os.path.join(basedir, "tahoe.cfg"), \ @@ -190,6 +208,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_reserved_3(self): + """ + reserved_space option understands 'mB' to mean megabytes + """ basedir = "client.Basic.test_reserved_3" os.mkdir(basedir) fileutil.write(os.path.join(basedir, "tahoe.cfg"), \ @@ -203,6 +224,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_reserved_4(self): + """ + reserved_space option understands 'Gb' to mean gigabytes + """ basedir = "client.Basic.test_reserved_4" os.mkdir(basedir) fileutil.write(os.path.join(basedir, "tahoe.cfg"), \ @@ -216,6 +240,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_reserved_bad(self): + """ + reserved_space option produces errors on non-numbers + """ basedir = "client.Basic.test_reserved_bad" os.mkdir(basedir) fileutil.write(os.path.join(basedir, "tahoe.cfg"), \ @@ -245,6 +272,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_web_staticdir(self): + """ + a relative web.static dir is expanded properly + """ basedir = u"client.Basic.test_web_staticdir" os.mkdir(basedir) fileutil.write(os.path.join(basedir, "tahoe.cfg"), @@ -281,6 +311,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_ftp_auth_keyfile(self): + """ + ftpd accounts.file is parsed properly + """ basedir = u"client.Basic.test_ftp_auth_keyfile" os.mkdir(basedir) fileutil.write(os.path.join(basedir, "tahoe.cfg"), @@ -296,6 +329,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_ftp_auth_url(self): + """ + ftpd accounts.url is parsed properly + """ basedir = u"client.Basic.test_ftp_auth_url" os.mkdir(basedir) fileutil.write(os.path.join(basedir, "tahoe.cfg"), @@ -309,6 +345,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_ftp_auth_no_accountfile_or_url(self): + """ + ftpd requires some way to look up accounts + """ basedir = u"client.Basic.test_ftp_auth_no_accountfile_or_url" os.mkdir(basedir) fileutil.write(os.path.join(basedir, "tahoe.cfg"), @@ -321,6 +360,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def _storage_dir_test(self, basedir, storage_path, expected_path): + """ + generic helper for following storage_dir tests + """ os.mkdir(basedir) cfg_path = os.path.join(basedir, "tahoe.cfg") fileutil.write( @@ -430,6 +472,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_versions(self): + """ + A client knows the versions of software it has + """ basedir = "test_client.Basic.test_versions" os.mkdir(basedir) fileutil.write(os.path.join(basedir, "tahoe.cfg"), \ @@ -453,6 +498,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_helper_furl(self): + """ + various helper.furl arguments are parsed correctly + """ basedir = "test_client.Basic.test_helper_furl" os.mkdir(basedir) @@ -473,6 +521,9 @@ class Basic(testutil.ReallyEqualMixin, testutil.NonASCIIPathMixin, unittest.Test @defer.inlineCallbacks def test_create_magic_folder_service(self): + """ + providing magic-folder options actually creates a MagicFolder service + """ boom = False class Boom(Exception): pass @@ -577,6 +628,9 @@ def flush_but_dont_ignore(res): class IntroducerClients(unittest.TestCase): def test_invalid_introducer_furl(self): + """ + An introducer.furl of 'None' is invalid + """ cfg = ( "[client]\n" "introducer.furl = None\n"