source: trunk/integration/test_i2p.py

Last change on this file was 67d5c82, checked in by meejah <meejah@…>, at 2023-07-29T19:34:12Z

codechecks / linter

  • Property mode set to 100644
File size: 6.8 KB
Line 
1"""
2Integration tests for I2P support.
3"""
4
5import sys
6from os.path import join, exists
7from os import mkdir, environ
8from time import sleep
9from shutil import which
10
11from eliot import log_call
12
13import pytest
14import pytest_twisted
15
16from . import util
17
18from twisted.python.filepath import (
19    FilePath,
20)
21from twisted.internet.error import ProcessExitedAlready
22
23from allmydata.test.common import (
24    write_introducer,
25)
26from allmydata.node import read_config
27from allmydata.util.iputil import allocate_tcp_port
28
29
30if which("docker") is None:
31    pytest.skip('Skipping I2P tests since Docker is unavailable', allow_module_level=True)
32# Docker on Windows machines sometimes expects Windows-y Docker images, so just
33# don't bother.
34if sys.platform.startswith('win'):
35    pytest.skip('Skipping I2P tests on Windows', allow_module_level=True)
36
37
38@pytest.fixture
39def i2p_network(reactor, temp_dir, request):
40    """Fixture to start up local i2pd."""
41    proto = util._MagicTextProtocol("ephemeral keys", "i2pd")
42    reactor.spawnProcess(
43        proto,
44        which("docker"),
45        (
46            "docker", "run", "-p", "7656:7656", "purplei2p/i2pd:release-2.45.1",
47            # Bad URL for reseeds, so it can't talk to other routers.
48            "--reseed.urls", "http://localhost:1/",
49            # Make sure we see the "ephemeral keys message"
50            "--log=stdout",
51            "--loglevel=info"
52        ),
53        env=environ,
54    )
55
56    def cleanup():
57        try:
58            proto.transport.signalProcess("INT")
59            util.block_with_timeout(proto.exited, reactor)
60        except ProcessExitedAlready:
61            pass
62    request.addfinalizer(cleanup)
63
64    util.block_with_timeout(proto.magic_seen, reactor, timeout=30)
65
66
67@pytest.fixture
68@log_call(
69    action_type=u"integration:i2p:introducer",
70    include_args=["temp_dir", "flog_gatherer"],
71    include_result=False,
72)
73def i2p_introducer(reactor, temp_dir, flog_gatherer, request):
74    intro_dir = join(temp_dir, 'introducer_i2p')
75    print("making introducer", intro_dir)
76
77    if not exists(intro_dir):
78        mkdir(intro_dir)
79        done_proto = util._ProcessExitedProtocol()
80        util._tahoe_runner_optional_coverage(
81            done_proto,
82            reactor,
83            request,
84            (
85                'create-introducer',
86                '--listen=i2p',
87                intro_dir,
88            ),
89        )
90        pytest_twisted.blockon(done_proto.done)
91
92    # over-write the config file with our stuff
93    config = read_config(intro_dir, "tub.port")
94    config.set_config("node", "nickname", "introducer_i2p")
95    config.set_config("node", "web.port", "4563")
96    config.set_config("node", "log_gatherer.furl", flog_gatherer)
97
98    # "tahoe run" is consistent across Linux/macOS/Windows, unlike the old
99    # "start" command.
100    protocol = util._MagicTextProtocol('introducer running', "introducer")
101    transport = util._tahoe_runner_optional_coverage(
102        protocol,
103        reactor,
104        request,
105        (
106            'run',
107            intro_dir,
108        ),
109    )
110
111    def cleanup():
112        try:
113            transport.signalProcess('TERM')
114            util.block_with_timeout(protocol.exited, reactor)
115        except ProcessExitedAlready:
116            pass
117    request.addfinalizer(cleanup)
118
119    pytest_twisted.blockon(protocol.magic_seen)
120    return transport
121
122
123@pytest.fixture
124def i2p_introducer_furl(i2p_introducer, temp_dir):
125    furl_fname = join(temp_dir, 'introducer_i2p', 'private', 'introducer.furl')
126    while not exists(furl_fname):
127        print("Don't see {} yet".format(furl_fname))
128        sleep(.1)
129    furl = open(furl_fname, 'r').read()
130    return furl
131
132
133@pytest_twisted.inlineCallbacks
134@pytest.mark.skip("I2P tests are not functioning at all, for unknown reasons")
135def test_i2p_service_storage(reactor, request, temp_dir, flog_gatherer, i2p_network, i2p_introducer_furl):
136    web_port0 = allocate_tcp_port()
137    web_port1 = allocate_tcp_port()
138    yield _create_anonymous_node(reactor, 'carol_i2p', web_port0, request, temp_dir, flog_gatherer, i2p_network, i2p_introducer_furl)
139    yield _create_anonymous_node(reactor, 'dave_i2p', web_port1, request, temp_dir, flog_gatherer, i2p_network, i2p_introducer_furl)
140    # ensure both nodes are connected to "a grid" by uploading
141    # something via carol, and retrieve it using dave.
142    gold_path = join(temp_dir, "gold")
143    with open(gold_path, "w") as f:
144        f.write(
145            "The object-capability model is a computer security model. A "
146            "capability describes a transferable right to perform one (or "
147            "more) operations on a given object."
148        )
149    # XXX could use treq or similar to POST these to their respective
150    # WUIs instead ...
151
152    proto = util._CollectOutputProtocol()
153    reactor.spawnProcess(
154        proto,
155        sys.executable,
156        (
157            sys.executable, '-b', '-m', 'allmydata.scripts.runner',
158            '-d', join(temp_dir, 'carol_i2p'),
159            'put', gold_path,
160        ),
161        env=environ,
162    )
163    yield proto.done
164    cap = proto.output.getvalue().strip().split()[-1]
165    print("TEH CAP!", cap)
166
167    proto = util._CollectOutputProtocol(capture_stderr=False)
168    reactor.spawnProcess(
169        proto,
170        sys.executable,
171        (
172            sys.executable, '-b', '-m', 'allmydata.scripts.runner',
173            '-d', join(temp_dir, 'dave_i2p'),
174            'get', cap,
175        ),
176        env=environ,
177    )
178    yield proto.done
179
180    dave_got = proto.output.getvalue().strip()
181    assert dave_got == open(gold_path, 'rb').read().strip()
182
183
184@pytest_twisted.inlineCallbacks
185def _create_anonymous_node(reactor, name, web_port, request, temp_dir, flog_gatherer, i2p_network, introducer_furl):
186    node_dir = FilePath(temp_dir).child(name)
187
188    print("creating", node_dir.path)
189    node_dir.makedirs()
190    proto = util._DumpOutputProtocol(None)
191    reactor.spawnProcess(
192        proto,
193        sys.executable,
194        (
195            sys.executable, '-b', '-m', 'allmydata.scripts.runner',
196            'create-node',
197            '--nickname', name,
198            '--introducer', introducer_furl,
199            '--hide-ip',
200            '--listen', 'i2p',
201            node_dir.path,
202        ),
203        env=environ,
204    )
205    yield proto.done
206
207
208    # Which services should this client connect to?
209    write_introducer(node_dir, "default", introducer_furl)
210    with node_dir.child('tahoe.cfg').open('w') as f:
211        node_config = '''
212[node]
213nickname = %(name)s
214web.port = %(web_port)s
215web.static = public_html
216log_gatherer.furl = %(log_furl)s
217
218[i2p]
219enabled = true
220
221[client]
222shares.needed = 1
223shares.happy = 1
224shares.total = 2
225
226''' % {
227    'name': name,
228    'web_port': web_port,
229    'log_furl': flog_gatherer,
230}
231        node_config = node_config.encode("utf-8")
232        f.write(node_config)
233
234    print("running")
235    yield util._run_node(reactor, node_dir.path, request, None)
236    print("okay, launched")
Note: See TracBrowser for help on using the repository browser.