From 1981bd68e64f0d541a3442bb30bcc15c448a5ac8 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 23 Aug 2019 16:16:30 +0200 Subject: [PATCH] [IMP] alter ngrok startup to allow concurrent runs Running multiple ngrok concurrently is only allowed from pro and up (OOTB and without shenanigans) is only allowed from Pro and up. However multiple tunnels through a single ngrok is allowed -> when tunneling through ngrok, start the process without any tunnel, use the API to create then remove the local tunnel, and shut down ngrok IIF there's no tunnel left. There's plenty of race conditions but given how slow tests are when they involve github that's probably not an issue. --- conftest.py | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/conftest.py b/conftest.py index 201be252..ac3628aa 100644 --- a/conftest.py +++ b/conftest.py @@ -4,9 +4,14 @@ import re import subprocess import time +import psutil import pytest import requests +NGROK_CLI = [ + 'ngrok', 'start', '--none', '--region', 'eu', +] + def pytest_addoption(parser): parser.addoption( '--tunnel', action="store", type="choice", choices=['ngrok', 'localtunnel'], default='ngrok', @@ -79,8 +84,27 @@ def tunnel(pytestconfig, port): tunnel = pytestconfig.getoption('--tunnel') if tunnel == 'ngrok': - p = subprocess.Popen(['ngrok', 'http', '--region', 'eu', str(port)], stdout=subprocess.DEVNULL) + addr = 'localhost:%d' % port + # if ngrok is not running, start it + try: + # FIXME: use config file so we can set web_addr to something else + # than localhost:4040 (otherwise we can't disambiguate + # between the ngrok we started and an ngrok started by + # some other user) + requests.get('http://localhost:4040/api') + except requests.exceptions.ConnectionError: + subprocess.Popen(NGROK_CLI, stdout=subprocess.DEVNULL) + time.sleep(1) + + requests.post('http://localhost:4040/api/tunnels', json={ + 'name': str(port), + 'proto': 'http', + 'bind_tls': True, + 'addr': addr, + 'inspect': False, + }) time.sleep(5) + try: r = requests.get('http://localhost:4040/api/tunnels') r.raise_for_status() @@ -88,10 +112,22 @@ def tunnel(pytestconfig, port): t['public_url'] for t in r.json()['tunnels'] if t['proto'] == 'https' + if t['config']['addr'].endswith(addr) ) finally: - p.terminate() - p.wait(30) + requests.delete('http://localhost:4040/api/tunnels/%s' % port) + time.sleep(5) # apparently tearing down the tunnel can take some time + r = requests.get('http://localhost:4040/api/tunnels') + if r.ok and r.json()['tunnels']: + return + + # ngrok is broken or all tunnels have been shut down -> try to + # find and kill it (but only if it looks a lot like we started it) + for p in psutil.process_iter(): + if p.name() == 'ngrok' and p.cmdline() == NGROK_CLI: + p.terminate() + break + elif tunnel == 'localtunnel': p = subprocess.Popen(['lt', '-p', str(port)], stdout=subprocess.PIPE) try: