[Pkg-privacy-commits] [onionbalance] 54/117: Add basic functional test for master descriptor publication

Donncha O'Cearbahill donncha-guest at moszumanska.debian.org
Wed Dec 16 23:18:46 UTC 2015


This is an automated email from the git hooks/post-receive script.

donncha-guest pushed a commit to branch debian/sid
in repository onionbalance.

commit e48649c759e9e7c0b20a99c389412079f688727f
Author: Donncha O'Cearbhaill <donncha at donncha.is>
Date:   Fri Jun 26 14:38:42 2015 +0100

    Add basic functional test for master descriptor publication
    
    The functional test creates a new master key and tries to create
    and upload a master descriptor using a Chutney created service as
    the instance. Stem is used to fetch the descriptor from Chutney HSDir
    and confirm that the master descriptor validates and contains all of
    the instance's introduction points.
    
    There is scope to expand this test to detect and assert if any warnings
    or errors are output while OnionBalance is running. More tests should be
    written to confirm the expected behavior when:
    
     * no instances are online
     * there are many (>10) instances configured
     * multiple services are configured
---
 test-requirements.txt                             |   1 +
 test/functional/test_publish_master_descriptor.py | 142 ++++++++++++++++++++++
 test/scripts/install-chutney.sh                   |   2 +
 tox.ini                                           |   4 +
 4 files changed, 149 insertions(+)

diff --git a/test-requirements.txt b/test-requirements.txt
index 1a6161b..53d243a 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,3 +1,4 @@
 pytest
 mock
 pytest-mock
+pexpect
diff --git a/test/functional/test_publish_master_descriptor.py b/test/functional/test_publish_master_descriptor.py
new file mode 100644
index 0000000..83e5978
--- /dev/null
+++ b/test/functional/test_publish_master_descriptor.py
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+import os
+import sys
+import socket
+import time
+
+import pytest
+import Crypto.PublicKey.RSA
+import yaml
+import pexpect
+import stem.control
+
+import onionbalance.util
+
+# Skip functional tests if Chutney environment is not running.
+pytestmark = pytest.mark.skipif(
+    "os.environ.get('CHUTNEY_ONION_ADDRESS') is None",
+    reason="Skipping functional test, no Chutney environment detected")
+
+
+def parse_chutney_enviroment():
+    """
+    Read environment variables and determine chutney instance and
+    client addresses.
+    """
+
+    tor_client = os.environ.get('CHUTNEY_CLIENT_PORT')
+    assert tor_client
+
+    # Calculate the address and port of clients control port
+    client_address, client_socks_port = tor_client.split(':')
+    client_ip = socket.gethostbyname(client_address)
+
+    tor_client_number = int(client_socks_port) - 9000
+    # Control port in the 8000-8999 range, offset by Tor client number
+    control_port = 8000 + tor_client_number
+    assert control_port
+
+    # Retrieve instance onion address exported during chutney setup
+    instance_address = os.environ.get('CHUTNEY_ONION_ADDRESS')
+    assert instance_address  # Need at least 1 instance address for test
+
+    if '.onion' in instance_address:
+        instance_address = instance_address[:16]
+
+    return {
+        'client_ip': client_ip,
+        'control_port': control_port,
+        'instances': [instance_address],
+    }
+
+
+def create_test_config_file(tmppath, private_key=None, instances=None):
+    """
+    Setup function to create a temp directory with master key and config file.
+    Returns a path to the temporary config file.
+
+    .. todo:: Refactor settings.py config creation to avoid code duplication
+              in integration tests.
+    """
+
+    if not private_key:
+        private_key = Crypto.PublicKey.RSA.generate(1024)
+
+    # Write private key file
+    key_path = tmppath.join('private_key')
+    key_path.write(private_key.exportKey())
+    assert key_path.check()
+
+    # Create YAML OnionBalance settings file for these instances
+    service_data = {'key': str(key_path)}
+    service_data['instances'] = [{'address': addr} for addr in instances]
+    settings_data = {'services': [service_data]}
+    config_yaml = yaml.dump(settings_data, default_flow_style=False)
+
+    config_path = tmppath.join('config.yaml')
+    config_path.write_binary(config_yaml.encode('utf-8'))
+    assert config_path.check()
+
+    return str(config_path)
+
+
+def test_master_descriptor_publication(tmpdir):
+    """
+    Functional test to run OnionBalance, publish a master descriptor and
+    check that it can be retrieved from the DHT.
+    """
+
+    chutney_config = parse_chutney_enviroment()
+    private_key = Crypto.PublicKey.RSA.generate(1024)
+    master_onion_address = onionbalance.util.calc_onion_address(private_key)
+
+    config_file_path = create_test_config_file(
+        tmppath=tmpdir,
+        private_key=private_key,
+        instances=chutney_config.get('instances', []),
+    )
+    assert config_file_path
+
+    # Start an OnionBalance server and monitor for correct output with pexpect
+    server = pexpect.spawnu("onionbalance",
+                            args=[
+                                '-i', chutney_config.get('client_ip'),
+                                '-p', str(chutney_config.get('control_port')),
+                                '-c', config_file_path,
+                                '-v', 'debug',
+                            ], logfile=sys.stdout, timeout=15)
+
+    # Check for expected output from OnionBalance
+    server.expect(u"Loaded the config file")
+    server.expect(u"Found new introduction points")
+    server.expect(u"Published a descriptor", timeout=120)
+
+    # Check Tor control port gave an uploaded event.
+    server.expect(u"HS_DESC UPLOADED")
+    # Eek, sleep to wait for descriptor upload to all replicas to finish
+    time.sleep(5)
+
+    # .. todo:: Also need to check and raise for any warnings or errors
+    #           that are emitted
+
+    # Try fetch and validate the descriptor with stem
+    with stem.control.Controller.from_port(
+        address=chutney_config.get('client_ip'),
+        port=chutney_config.get('control_port')
+    ) as controller:
+        controller.authenticate()
+
+        # get_hidden_service_descriptor() will raise exceptions if it
+        # cannot find the descriptors
+        master_descriptor = controller.get_hidden_service_descriptor(
+            master_onion_address)
+        master_ips = master_descriptor.introduction_points()
+
+        # Try retrieve a descriptor for each instance
+        for instance_address in chutney_config.get('instances'):
+            instance_descriptor = controller.get_hidden_service_descriptor(
+                instance_address)
+            instance_ips = instance_descriptor.introduction_points()
+
+            # Check if all instance IPs were included in the master descriptor
+            assert all(ip in instance_ips for ip in master_ips)
diff --git a/test/scripts/install-chutney.sh b/test/scripts/install-chutney.sh
index e8d0b48..2a5167f 100755
--- a/test/scripts/install-chutney.sh
+++ b/test/scripts/install-chutney.sh
@@ -3,6 +3,8 @@
 # service system to be available.
 git clone https://github.com/DonnchaC/chutney.git
 cd chutney
+# Stop chutney network if it is already running
+./chutney stop networks/hs
 ./chutney configure networks/hs
 ./chutney start networks/hs
 ./chutney status networks/hs
diff --git a/tox.ini b/tox.ini
index cc8a29a..bcc0353 100644
--- a/tox.ini
+++ b/tox.ini
@@ -9,6 +9,10 @@ envlist = style, py27, py34, docs
 [testenv]
 deps = -rrequirements.txt
        -rtest-requirements.txt
+# Pass Chutney enviroment variables into tox virtual enviroments.
+setenv =
+    CHUTNEY_ONION_ADDRESS = {env:CHUTNEY_ONION_ADDRESS}
+    CHUTNEY_CLIENT_PORT = {env:CHUTNEY_CLIENT_PORT}
 commands = py.test
 
 [testenv:docs]

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/onionbalance.git



More information about the Pkg-privacy-commits mailing list