diff --git a/src/allmydata/scripts/create_node.py b/src/allmydata/scripts/create_node.py index 95160c710..41002deb7 100644 --- a/src/allmydata/scripts/create_node.py +++ b/src/allmydata/scripts/create_node.py @@ -92,6 +92,11 @@ def validate_where_options(o): def validate_tor_options(o): use_tor = "tor" in o["listen"].split(",") + if use_tor or any((o["tor-launch"], o["tor-control-port"])): + if tor_provider._import_txtorcon() is None: + raise UsageError( + "Specifying any Tor options requires the 'txtorcon' module" + ) if not use_tor: if o["tor-launch"]: raise UsageError("--tor-launch requires --listen=tor") @@ -102,6 +107,11 @@ def validate_tor_options(o): def validate_i2p_options(o): use_i2p = "i2p" in o["listen"].split(",") + if use_i2p or any((o["i2p-launch"], o["i2p-sam-port"])): + if i2p_provider._import_txi2p() is None: + raise UsageError( + "Specifying any I2P options requires the 'txi2p' module" + ) if not use_i2p: if o["i2p-launch"]: raise UsageError("--i2p-launch requires --listen=i2p") @@ -117,6 +127,16 @@ class _CreateBaseOptions(BasedirOptions): ("hide-ip", None, "prohibit any configuration that would reveal the node's IP address"), ] + def postOptions(self): + super(_CreateBaseOptions, self).postOptions() + if self['hide-ip']: + if tor_provider._import_txtorcon() is None and i2p_provider._import_txi2p() is None: + raise UsageError( + "--hide-ip was specified but neither 'txtorcon' nor 'txi2p' " + "are installed.\nTo do so:\n pip install tahoe-lafs[tor]\nor\n" + " pip install tahoe-lafs[i2p]" + ) + class CreateClientOptions(_CreateBaseOptions): synopsis = "[options] [NODEDIR]" description = "Create a client-only Tahoe-LAFS node (no storage server)." @@ -154,6 +174,7 @@ class CreateNodeOptions(CreateClientOptions): validate_tor_options(self) validate_i2p_options(self) + class CreateIntroducerOptions(NoDefaultBasedirOptions): subcommand_name = "create-introducer" description = "Create a Tahoe-LAFS introducer." @@ -167,6 +188,7 @@ class CreateIntroducerOptions(NoDefaultBasedirOptions): validate_tor_options(self) validate_i2p_options(self) + @defer.inlineCallbacks def write_node_config(c, config): # this is shared between clients and introducers diff --git a/src/allmydata/test/cli/test_create.py b/src/allmydata/test/cli/test_create.py index b9cd5e379..444370cfa 100644 --- a/src/allmydata/test/cli/test_create.py +++ b/src/allmydata/test/cli/test_create.py @@ -43,10 +43,43 @@ class Config(unittest.TestCase): self.assertEqual(cfg.get("node", "tub.location"), "disabled") self.assertFalse(cfg.has_section("connections")) + @defer.inlineCallbacks + def test_client_hide_ip_no_i2p_txtorcon(self): + # hmm, I must be doing something weird, these don't work as + # @mock.patch decorators for some reason + txi2p = mock.patch('allmydata.util.i2p_provider._import_txi2p', return_value=None) + txtorcon = mock.patch('allmydata.util.tor_provider._import_txtorcon', return_value=None) + with txi2p, txtorcon: + basedir = self.mktemp() + rc, out, err = yield run_cli("create-client", "--hide-ip", basedir) + print(rc, out, err) + self.assertTrue(rc != 0, out) + self.assertTrue('pip install tahoe-lafs[i2p]' in out) + self.assertTrue('pip install tahoe-lafs[tor]' in out) + + @defer.inlineCallbacks + def test_client_i2p_option_no_txi2p(self): + txi2p = mock.patch('allmydata.util.i2p_provider._import_txi2p', return_value=None) + with txi2p: + basedir = self.mktemp() + rc, out, err = yield run_cli("create-node", "--listen=i2p", "--i2p-launch", basedir) + self.assertTrue(rc != 0) + self.assertTrue("Specifying any I2P options requires the 'txi2p' module" in out) + + @defer.inlineCallbacks + def test_client_tor_option_no_txtorcon(self): + txtorcon = mock.patch('allmydata.util.tor_provider._import_txtorcon', return_value=None) + with txtorcon: + basedir = self.mktemp() + rc, out, err = yield run_cli("create-node", "--listen=tor", "--tor-launch", basedir) + self.assertTrue(rc != 0) + self.assertTrue("Specifying any Tor options requires the 'txtorcon' module" in out) + @defer.inlineCallbacks def test_client_hide_ip(self): basedir = self.mktemp() rc, out, err = yield run_cli("create-client", "--hide-ip", basedir) + self.assertEqual(0, rc) cfg = read_config(basedir) self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), False) self.assertEqual(cfg.get("connections", "tcp"), "tor")