6844: Clean up websocket tests, fix possible deadlock, reduce unnecessary sleep().
[arvados.git] / sdk / python / tests / run_test_server.py
index b25bf6215a7a90fcea20ef63995ad026c75b5d3a..1c5162b97d87e476b9ff3badc82ffce3afdd4d28 100644 (file)
@@ -115,6 +115,33 @@ def find_available_port():
     sock.close()
     return port
 
+def _wait_until_port_listens(port, timeout=10):
+    """Wait for a process to start listening on the given port.
+
+    If nothing listens on the port within the specified timeout (given
+    in seconds), print a warning on stderr before returning.
+    """
+    try:
+        subprocess.check_output(['which', 'lsof'])
+    except subprocess.CalledProcessError:
+        print("WARNING: No `lsof` -- cannot wait for port to listen. "+
+              "Sleeping 0.5 and hoping for the best.")
+        time.sleep(0.5)
+        return
+    deadline = time.time() + timeout
+    while time.time() < deadline:
+        try:
+            subprocess.check_output(
+                ['lsof', '-t', '-i', 'tcp:'+str(port)])
+        except subprocess.CalledProcessError:
+            time.sleep(0.1)
+            continue
+        return
+    print(
+        "WARNING: Nothing is listening on port {} (waited {} seconds).".
+        format(port, timeout),
+        file=sys.stderr)
+
 def run(leave_running_atexit=False):
     """Ensure an API server is running, and ARVADOS_API_* env vars have
     admin credentials for it.
@@ -224,8 +251,10 @@ def run(leave_running_atexit=False):
     my_api_host = match.group(1)
     os.environ['ARVADOS_API_HOST'] = my_api_host
 
-    # Make sure the server has written its pid file before continuing
+    # Make sure the server has written its pid file and started
+    # listening on its TCP port
     find_server_pid(pid_file)
+    _wait_until_port_listens(port)
 
     reset()
     os.chdir(restore_cwd)
@@ -289,6 +318,8 @@ def _start_keep(n, keep_args):
     with open("{}/keep{}.volume".format(TEST_TMPDIR, n), 'w') as f:
         f.write(keep0)
 
+    _wait_until_port_listens(port)
+
     return port
 
 def run_keep(blob_signing_key=None, enforce_permissions=False):
@@ -369,6 +400,7 @@ def run_keep_proxy():
     }}).execute()
     os.environ["ARVADOS_KEEP_PROXY"] = "http://localhost:{}".format(port)
     _setport('keepproxy', port)
+    _wait_until_port_listens(port)
 
 def stop_keep_proxy():
     if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
@@ -392,6 +424,7 @@ def run_arv_git_httpd():
     with open(_pidfile('arv-git-httpd'), 'w') as f:
         f.write(str(agh.pid))
     _setport('arv-git-httpd', gitport)
+    _wait_until_port_listens(gitport)
 
 def stop_arv_git_httpd():
     if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
@@ -453,10 +486,13 @@ def _getport(program):
 def _apiconfig(key):
     if _cached_config:
         return _cached_config[key]
-    def _load(f):
-        return yaml.load(os.path.join(SERVICES_SRC_DIR, 'api', 'config', f))
+    def _load(f, required=True):
+        fullpath = os.path.join(SERVICES_SRC_DIR, 'api', 'config', f)
+        if not required and not os.path.exists(fullpath):
+            return {}
+        return yaml.load(fullpath)
     cdefault = _load('application.default.yml')
-    csite = _load('application.yml')
+    csite = _load('application.yml', required=False)
     _cached_config = {}
     for section in [cdefault.get('common',{}), cdefault.get('test',{}),
                     csite.get('common',{}), csite.get('test',{})]: