X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/83203f5c739ee0b0199e76babccb60e832a0de8e..bdabe39ff5360f904de323cd850195237179dcaf:/sdk/python/tests/run_test_server.py diff --git a/sdk/python/tests/run_test_server.py b/sdk/python/tests/run_test_server.py index da35f4fc9b..0cb4151ac3 100644 --- a/sdk/python/tests/run_test_server.py +++ b/sdk/python/tests/run_test_server.py @@ -1,6 +1,11 @@ -#!/usr/bin/env python +# Copyright (C) The Arvados Authors. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 from __future__ import print_function +from __future__ import division +from builtins import str +from builtins import range import argparse import atexit import errno @@ -21,6 +26,11 @@ import time import unittest import yaml +try: + from urllib.parse import urlparse +except ImportError: + from urlparse import urlparse + MY_DIRNAME = os.path.dirname(os.path.realpath(__file__)) if __name__ == '__main__' and os.path.exists( os.path.join(MY_DIRNAME, '..', 'arvados', '__init__.py')): @@ -33,6 +43,14 @@ import arvados.config ARVADOS_DIR = os.path.realpath(os.path.join(MY_DIRNAME, '../../..')) SERVICES_SRC_DIR = os.path.join(ARVADOS_DIR, 'services') + +# Work around https://bugs.python.org/issue27805, should be no longer +# necessary from sometime in Python 3.8.x +if not os.environ.get('ARVADOS_DEBUG', ''): + WRITE_MODE = 'a' +else: + WRITE_MODE = 'w' + if 'GOPATH' in os.environ: # Add all GOPATH bin dirs to PATH -- but insert them after the # ruby gems bin dir, to ensure "bundle" runs the Ruby bundler @@ -96,7 +114,7 @@ def kill_server_pid(pidfile, wait=10, passenger_root=False): # Use up to half of the +wait+ period waiting for "passenger # stop" to work. If the process hasn't exited by then, start # sending TERM signals. - startTERM += wait/2 + startTERM += wait//2 server_pid = None while now <= deadline and server_pid is None: @@ -169,51 +187,69 @@ def find_available_port(): sock.close() return port -def _wait_until_port_listens(port, timeout=10): +def _wait_until_port_listens(port, timeout=10, warn=True): """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']) + subprocess.check_output(['which', 'netstat']) except subprocess.CalledProcessError: - print("WARNING: No `lsof` -- cannot wait for port to listen. "+ + print("WARNING: No `netstat` -- cannot wait for port to listen. "+ "Sleeping 0.5 and hoping for the best.", file=sys.stderr) 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) + if re.search(r'\ntcp.*:'+str(port)+' .* LISTEN *\n', subprocess.check_output(['netstat', '-Wln']).decode()): + return True + time.sleep(0.1) + if warn: + print( + "WARNING: Nothing is listening on port {} (waited {} seconds).". + format(port, timeout), + file=sys.stderr) + return False -def _fifo2stderr(label): - """Create a fifo, and copy it to stderr, prepending label to each line. +def _logfilename(label): + """Set up a labelled log file, and return a path to write logs to. - Return value is the path to the new FIFO. + Normally, the returned path is {tmpdir}/{label}.log. + + In debug mode, logs are also written to stderr, with [label] + prepended to each line. The returned path is a FIFO. +label+ should contain only alphanumerics: it is also used as part of the FIFO filename. + """ + logfilename = os.path.join(TEST_TMPDIR, label+'.log') + if not os.environ.get('ARVADOS_DEBUG', ''): + return logfilename fifo = os.path.join(TEST_TMPDIR, label+'.fifo') try: os.remove(fifo) except OSError as error: if error.errno != errno.ENOENT: raise - os.mkfifo(fifo, 0700) + os.mkfifo(fifo, 0o700) + stdbuf = ['stdbuf', '-i0', '-oL', '-eL'] + # open(fifo, 'r') would block waiting for someone to open the fifo + # for writing, so we need a separate cat process to open it for + # us. + cat = subprocess.Popen( + stdbuf+['cat', fifo], + stdin=open('/dev/null'), + stdout=subprocess.PIPE) + tee = subprocess.Popen( + stdbuf+['tee', '-a', logfilename], + stdin=cat.stdout, + stdout=subprocess.PIPE) subprocess.Popen( - ['stdbuf', '-i0', '-oL', '-eL', 'sed', '-e', 's/^/['+label+'] /', fifo], + stdbuf+['sed', '-e', 's/^/['+label+'] /'], + stdin=tee.stdout, stdout=sys.stderr) return fifo @@ -239,8 +275,9 @@ def run(leave_running_atexit=False): # This will clear cached docs that belong to other processes (like # concurrent test suites) even if they're still running. They should # be able to tolerate that. - for fn in glob.glob(os.path.join(arvados.http_cache('discovery'), - '*,arvados,v1,rest,*')): + for fn in glob.glob(os.path.join( + str(arvados.http_cache('discovery')), + '*,arvados,v1,rest,*')): os.unlink(fn) pid_file = _pidfile('api') @@ -282,21 +319,6 @@ def run(leave_running_atexit=False): if not os.path.exists('tmp/logs'): os.makedirs('tmp/logs') - if not os.path.exists('tmp/self-signed.pem'): - # We assume here that either passenger reports its listening - # address as https:/0.0.0.0:port/. If it reports "127.0.0.1" - # then the certificate won't match the host and reset() will - # fail certificate verification. If it reports "localhost", - # clients (notably Python SDK's websocket client) might - # resolve localhost as ::1 and then fail to connect. - subprocess.check_call([ - 'openssl', 'req', '-new', '-x509', '-nodes', - '-out', 'tmp/self-signed.pem', - '-keyout', 'tmp/self-signed.key', - '-days', '3650', - '-subj', '/CN=0.0.0.0'], - stdout=sys.stderr) - # Install the git repository fixtures. gitdir = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'git') gittarball = os.path.join(SERVICES_SRC_DIR, 'api', 'test', 'test.git.tar') @@ -304,47 +326,36 @@ def run(leave_running_atexit=False): os.makedirs(gitdir) subprocess.check_output(['tar', '-xC', gitdir, '-f', gittarball]) - # The nginx proxy isn't listening here yet, but we need to choose - # the wss:// port now so we can write the API server config file. - wss_port = find_available_port() - _setport('wss', wss_port) - - port = find_available_port() + port = internal_port_from_config("RailsAPI") env = os.environ.copy() env['RAILS_ENV'] = 'test' - env['ARVADOS_TEST_WSS_PORT'] = str(wss_port) - if env.get('ARVADOS_TEST_EXPERIMENTAL_WS'): - env.pop('ARVADOS_WEBSOCKETS', None) - else: - env['ARVADOS_WEBSOCKETS'] = 'yes' + env['ARVADOS_RAILS_LOG_TO_STDOUT'] = '1' + env.pop('ARVADOS_WEBSOCKETS', None) env.pop('ARVADOS_TEST_API_HOST', None) env.pop('ARVADOS_API_HOST', None) env.pop('ARVADOS_API_HOST_INSECURE', None) env.pop('ARVADOS_API_TOKEN', None) - start_msg = subprocess.check_output( + logf = open(_logfilename('railsapi'), WRITE_MODE) + railsapi = subprocess.Popen( ['bundle', 'exec', - 'passenger', 'start', '-d', '-p{}'.format(port), + 'passenger', 'start', '-p{}'.format(port), '--pid-file', pid_file, - '--log-file', os.path.join(os.getcwd(), 'log/test.log'), + '--log-file', '/dev/stdout', '--ssl', '--ssl-certificate', 'tmp/self-signed.pem', '--ssl-certificate-key', 'tmp/self-signed.key'], - env=env) + env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf) if not leave_running_atexit: atexit.register(kill_server_pid, pid_file, passenger_root=api_src_dir) - match = re.search(r'Accessible via: https://(.*?)/', start_msg) - if not match: - raise Exception( - "Passenger did not report endpoint: {}".format(start_msg)) - my_api_host = match.group(1) + my_api_host = "127.0.0.1:"+str(port) os.environ['ARVADOS_API_HOST'] = my_api_host # 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) + find_server_pid(pid_file) reset() os.chdir(restore_cwd) @@ -365,10 +376,11 @@ def reset(): httpclient.request( 'https://{}/database/reset'.format(existing_api_host), 'POST', - headers={'Authorization': 'OAuth2 {}'.format(token)}) + headers={'Authorization': 'OAuth2 {}'.format(token), 'Connection':'close'}) + os.environ['ARVADOS_API_HOST_INSECURE'] = 'true' - os.environ['ARVADOS_API_HOST'] = existing_api_host os.environ['ARVADOS_API_TOKEN'] = token + os.environ['ARVADOS_API_HOST'] = existing_api_host def stop(force=False): """Stop the API server, if one is running. @@ -389,40 +401,49 @@ def stop(force=False): kill_server_pid(_pidfile('api')) my_api_host = None +def get_config(): + with open(os.environ["ARVADOS_CONFIG"]) as f: + return yaml.safe_load(f) + +def internal_port_from_config(service, idx=0): + return int(urlparse( + sorted(list(get_config()["Clusters"]["zzzzz"]["Services"][service]["InternalURLs"].keys()))[idx]). + netloc.split(":")[1]) + +def external_port_from_config(service): + return int(urlparse(get_config()["Clusters"]["zzzzz"]["Services"][service]["ExternalURL"]).netloc.split(":")[1]) + +def run_controller(): + if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ: + return + stop_controller() + logf = open(_logfilename('controller'), WRITE_MODE) + port = internal_port_from_config("Controller") + controller = subprocess.Popen( + ["arvados-server", "controller"], + stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True) + with open(_pidfile('controller'), 'w') as f: + f.write(str(controller.pid)) + _wait_until_port_listens(port) + return port + +def stop_controller(): + if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ: + return + kill_server_pid(_pidfile('controller')) + def run_ws(): if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ: return stop_ws() - port = find_available_port() - conf = os.path.join(TEST_TMPDIR, 'ws.yml') - with open(conf, 'w') as f: - f.write(""" -Client: - APIHost: {} - Insecure: true -Listen: :{} -LogLevel: {} -Postgres: - host: {} - dbname: {} - user: {} - password: {} - sslmode: require - """.format(os.environ['ARVADOS_API_HOST'], - port, - ('info' if os.environ.get('ARVADOS_DEBUG', '') in ['','0'] else 'debug'), - _dbconfig('host'), - _dbconfig('database'), - _dbconfig('username'), - _dbconfig('password'))) - logf = open(_fifo2stderr('ws'), 'w') + port = internal_port_from_config("Websocket") + logf = open(_logfilename('ws'), WRITE_MODE) ws = subprocess.Popen( - ["ws", "-config", conf], + ["arvados-server", "ws"], stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True) with open(_pidfile('ws'), 'w') as f: f.write(str(ws.pid)) _wait_until_port_listens(port) - _setport('ws', port) return port def stop_ws(): @@ -430,46 +451,41 @@ def stop_ws(): return kill_server_pid(_pidfile('ws')) -def _start_keep(n, keep_args): - keep0 = tempfile.mkdtemp() - port = find_available_port() - keep_cmd = ["keepstore", - "-volume={}".format(keep0), - "-listen=:{}".format(port), - "-pid="+_pidfile('keep{}'.format(n))] - - for arg, val in keep_args.iteritems(): - keep_cmd.append("{}={}".format(arg, val)) +def _start_keep(n, blob_signing=False): + datadir = os.path.join(TEST_TMPDIR, "keep%d.data"%n) + if os.path.exists(datadir): + shutil.rmtree(datadir) + os.mkdir(datadir) + port = internal_port_from_config("Keepstore", idx=n) + + # Currently, if there are multiple InternalURLs for a single host, + # the only way to tell a keepstore process which one it's supposed + # to listen on is to supply a redacted version of the config, with + # the other InternalURLs removed. + conf = os.path.join(TEST_TMPDIR, "keep%d.yaml"%n) + confdata = get_config() + confdata['Clusters']['zzzzz']['Services']['Keepstore']['InternalURLs'] = {"http://127.0.0.1:%d"%port: {}} + confdata['Clusters']['zzzzz']['Collections']['BlobSigning'] = blob_signing + with open(conf, 'w') as f: + yaml.safe_dump(confdata, f) + keep_cmd = ["keepstore", "-config", conf] - logf = open(_fifo2stderr('keep{}'.format(n)), 'w') - kp0 = subprocess.Popen( - keep_cmd, stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True) + with open(_logfilename('keep{}'.format(n)), WRITE_MODE) as logf: + with open('/dev/null') as _stdin: + child = subprocess.Popen( + keep_cmd, stdin=_stdin, stdout=logf, stderr=logf, close_fds=True) + print('child.pid is %d'%child.pid, file=sys.stderr) with open(_pidfile('keep{}'.format(n)), 'w') as f: - f.write(str(kp0.pid)) - - with open("{}/keep{}.volume".format(TEST_TMPDIR, n), 'w') as f: - f.write(keep0) + f.write(str(child.pid)) _wait_until_port_listens(port) return port -def run_keep(blob_signing_key=None, enforce_permissions=False, num_servers=2): +def run_keep(num_servers=2, **kwargs): stop_keep(num_servers) - keep_args = {} - if not blob_signing_key: - blob_signing_key = 'zfhgfenhffzltr9dixws36j1yhksjoll2grmku38mi7yxd66h5j4q9w4jzanezacp8s6q0ro3hxakfye02152hncy6zml2ed0uc' - with open(os.path.join(TEST_TMPDIR, "keep.blob_signing_key"), "w") as f: - keep_args['-blob-signing-key-file'] = f.name - f.write(blob_signing_key) - keep_args['-enforce-permissions'] = str(enforce_permissions).lower() - with open(os.path.join(TEST_TMPDIR, "keep.data-manager-token-file"), "w") as f: - keep_args['-data-manager-token-file'] = f.name - f.write(auth_token('data_manager')) - keep_args['-never-delete'] = 'false' - api = arvados.api( version='v1', host=os.environ['ARVADOS_API_HOST'], @@ -482,7 +498,7 @@ def run_keep(blob_signing_key=None, enforce_permissions=False, num_servers=2): api.keep_disks().delete(uuid=d['uuid']).execute() for d in range(0, num_servers): - port = _start_keep(d, keep_args) + port = _start_keep(d, **kwargs) svc = api.keep_services().create(body={'keep_service': { 'uuid': 'zzzzz-bi6l4-keepdisk{:07d}'.format(d), 'service_host': 'localhost', @@ -494,23 +510,19 @@ def run_keep(blob_signing_key=None, enforce_permissions=False, num_servers=2): 'keep_disk': {'keep_service_uuid': svc['uuid'] } }).execute() - # If keepproxy is running, send SIGHUP to make it discover the new - # keepstore services. - proxypidfile = _pidfile('keepproxy') - if os.path.exists(proxypidfile): - try: - os.kill(int(open(proxypidfile).read()), signal.SIGHUP) - except OSError: - os.remove(proxypidfile) + # If keepproxy and/or keep-web is running, send SIGHUP to make + # them discover the new keepstore services. + for svc in ('keepproxy', 'keep-web'): + pidfile = _pidfile('keepproxy') + if os.path.exists(pidfile): + try: + with open(pidfile) as pid: + os.kill(int(pid.read()), signal.SIGHUP) + except OSError: + os.remove(pidfile) def _stop_keep(n): kill_server_pid(_pidfile('keep{}'.format(n))) - if os.path.exists("{}/keep{}.volume".format(TEST_TMPDIR, n)): - with open("{}/keep{}.volume".format(TEST_TMPDIR, n), 'r') as r: - shutil.rmtree(r.read(), True) - os.unlink("{}/keep{}.volume".format(TEST_TMPDIR, n)) - if os.path.exists(os.path.join(TEST_TMPDIR, "keep.blob_signing_key")): - os.remove(os.path.join(TEST_TMPDIR, "keep.blob_signing_key")) def stop_keep(num_servers=2): for n in range(0, num_servers): @@ -518,19 +530,22 @@ def stop_keep(num_servers=2): def run_keep_proxy(): if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ: + os.environ["ARVADOS_KEEP_SERVICES"] = "http://localhost:{}".format(internal_port_from_config('Keepproxy')) return stop_keep_proxy() - port = find_available_port() + port = internal_port_from_config("Keepproxy") env = os.environ.copy() env['ARVADOS_API_TOKEN'] = auth_token('anonymous') - logf = open(_fifo2stderr('keepproxy'), 'w') + logf = open(_logfilename('keepproxy'), WRITE_MODE) kp = subprocess.Popen( - ['keepproxy', - '-pid='+_pidfile('keepproxy'), - '-listen=:{}'.format(port)], - env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True) + ['keepproxy'], env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True) + with open(_pidfile('keepproxy'), 'w') as f: + f.write(str(kp.pid)) + _wait_until_port_listens(port) + + print("Using API %s token %s" % (os.environ['ARVADOS_API_HOST'], auth_token('admin')), file=sys.stdout) api = arvados.api( version='v1', host=os.environ['ARVADOS_API_HOST'], @@ -546,7 +561,6 @@ def run_keep_proxy(): 'service_ssl_flag': False, }}).execute() os.environ["ARVADOS_KEEP_SERVICES"] = "http://localhost:{}".format(port) - _setport('keepproxy', port) _wait_until_port_listens(port) def stop_keep_proxy(): @@ -559,19 +573,14 @@ def run_arv_git_httpd(): return stop_arv_git_httpd() - gitdir = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'git') - gitport = find_available_port() + gitport = internal_port_from_config("GitHTTP") env = os.environ.copy() env.pop('ARVADOS_API_TOKEN', None) - logf = open(_fifo2stderr('arv-git-httpd'), 'w') - agh = subprocess.Popen( - ['arv-git-httpd', - '-repo-root='+gitdir+'/test', - '-address=:'+str(gitport)], + logf = open(_logfilename('arv-git-httpd'), WRITE_MODE) + agh = subprocess.Popen(['arv-git-httpd'], env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf) 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(): @@ -584,19 +593,14 @@ def run_keep_web(): return stop_keep_web() - keepwebport = find_available_port() + keepwebport = internal_port_from_config("WebDAV") env = os.environ.copy() - env['ARVADOS_API_TOKEN'] = auth_token('anonymous') - logf = open(_fifo2stderr('keep-web'), 'w') + logf = open(_logfilename('keep-web'), WRITE_MODE) keepweb = subprocess.Popen( - ['keep-web', - '-allow-anonymous', - '-attachment-only-host=download:'+str(keepwebport), - '-listen=:'+str(keepwebport)], + ['keep-web'], env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf) with open(_pidfile('keep-web'), 'w') as f: f.write(str(keepweb.pid)) - _setport('keep-web', keepwebport) _wait_until_port_listens(keepwebport) def stop_keep_web(): @@ -609,24 +613,33 @@ def run_nginx(): return stop_nginx() nginxconf = {} - nginxconf['KEEPWEBPORT'] = _getport('keep-web') - nginxconf['KEEPWEBDLSSLPORT'] = find_available_port() - nginxconf['KEEPWEBSSLPORT'] = find_available_port() - nginxconf['KEEPPROXYPORT'] = _getport('keepproxy') - nginxconf['KEEPPROXYSSLPORT'] = find_available_port() - nginxconf['GITPORT'] = _getport('arv-git-httpd') - nginxconf['GITSSLPORT'] = find_available_port() - nginxconf['WSPORT'] = _getport('ws') - nginxconf['WSSPORT'] = _getport('wss') + nginxconf['LISTENHOST'] = 'localhost' + nginxconf['CONTROLLERPORT'] = internal_port_from_config("Controller") + nginxconf['CONTROLLERSSLPORT'] = external_port_from_config("Controller") + nginxconf['KEEPWEBPORT'] = internal_port_from_config("WebDAV") + nginxconf['KEEPWEBDLSSLPORT'] = external_port_from_config("WebDAVDownload") + nginxconf['KEEPWEBSSLPORT'] = external_port_from_config("WebDAV") + nginxconf['KEEPPROXYPORT'] = internal_port_from_config("Keepproxy") + nginxconf['KEEPPROXYSSLPORT'] = external_port_from_config("Keepproxy") + nginxconf['GITPORT'] = internal_port_from_config("GitHTTP") + nginxconf['GITSSLPORT'] = external_port_from_config("GitHTTP") + nginxconf['HEALTHPORT'] = internal_port_from_config("Health") + nginxconf['HEALTHSSLPORT'] = external_port_from_config("Health") + nginxconf['WSPORT'] = internal_port_from_config("Websocket") + nginxconf['WSSSLPORT'] = external_port_from_config("Websocket") + nginxconf['WORKBENCH1PORT'] = internal_port_from_config("Workbench1") + nginxconf['WORKBENCH1SSLPORT'] = external_port_from_config("Workbench1") nginxconf['SSLCERT'] = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.pem') nginxconf['SSLKEY'] = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.key') - nginxconf['ACCESSLOG'] = _fifo2stderr('nginx_access_log') + nginxconf['ACCESSLOG'] = _logfilename('nginx_access') + nginxconf['ERRORLOG'] = _logfilename('nginx_error') + nginxconf['TMPDIR'] = TEST_TMPDIR conftemplatefile = os.path.join(MY_DIRNAME, 'nginx.conf') conffile = os.path.join(TEST_TMPDIR, 'nginx.conf') with open(conffile, 'w') as f: f.write(re.sub( - r'{{([A-Z]+)}}', + r'{{([A-Z]+[A-Z0-9]+)}}', lambda match: str(nginxconf.get(match.group(1))), open(conftemplatefile).read())) @@ -639,10 +652,175 @@ def run_nginx(): '-g', 'pid '+_pidfile('nginx')+';', '-c', conffile], env=env, stdin=open('/dev/null'), stdout=sys.stderr) - _setport('keep-web-dl-ssl', nginxconf['KEEPWEBDLSSLPORT']) - _setport('keep-web-ssl', nginxconf['KEEPWEBSSLPORT']) - _setport('keepproxy-ssl', nginxconf['KEEPPROXYSSLPORT']) - _setport('arv-git-httpd-ssl', nginxconf['GITSSLPORT']) + _wait_until_port_listens(nginxconf['CONTROLLERSSLPORT']) + +def setup_config(): + rails_api_port = find_available_port() + controller_port = find_available_port() + controller_external_port = find_available_port() + websocket_port = find_available_port() + websocket_external_port = find_available_port() + workbench1_port = find_available_port() + workbench1_external_port = find_available_port() + git_httpd_port = find_available_port() + git_httpd_external_port = find_available_port() + health_httpd_port = find_available_port() + health_httpd_external_port = find_available_port() + keepproxy_port = find_available_port() + keepproxy_external_port = find_available_port() + keepstore_ports = sorted([str(find_available_port()) for _ in range(0,4)]) + keep_web_port = find_available_port() + keep_web_external_port = find_available_port() + keep_web_dl_port = find_available_port() + keep_web_dl_external_port = find_available_port() + + configsrc = os.environ.get("CONFIGSRC", None) + if configsrc: + clusterconf = os.path.join(configsrc, "config.yml") + print("Getting config from %s" % clusterconf, file=sys.stderr) + pgconnection = yaml.safe_load(open(clusterconf))["Clusters"]["zzzzz"]["PostgreSQL"]["Connection"] + else: + # assume "arvados-server install -type test" has set up the + # conventional db credentials + pgconnection = { + "client_encoding": "utf8", + "host": "localhost", + "dbname": "arvados_test", + "user": "arvados", + "password": "insecure_arvados_test", + } + + localhost = "127.0.0.1" + services = { + "RailsAPI": { + "InternalURLs": { + "https://%s:%s"%(localhost, rails_api_port): {}, + }, + }, + "Controller": { + "ExternalURL": "https://%s:%s" % (localhost, controller_external_port), + "InternalURLs": { + "http://%s:%s"%(localhost, controller_port): {}, + }, + }, + "Websocket": { + "ExternalURL": "wss://%s:%s/websocket" % (localhost, websocket_external_port), + "InternalURLs": { + "http://%s:%s"%(localhost, websocket_port): {}, + }, + }, + "Workbench1": { + "ExternalURL": "https://%s:%s/" % (localhost, workbench1_external_port), + "InternalURLs": { + "http://%s:%s"%(localhost, workbench1_port): {}, + }, + }, + "GitHTTP": { + "ExternalURL": "https://%s:%s" % (localhost, git_httpd_external_port), + "InternalURLs": { + "http://%s:%s"%(localhost, git_httpd_port): {} + }, + }, + "Health": { + "ExternalURL": "https://%s:%s" % (localhost, health_httpd_external_port), + "InternalURLs": { + "http://%s:%s"%(localhost, health_httpd_port): {} + }, + }, + "Keepstore": { + "InternalURLs": { + "http://%s:%s"%(localhost, port): {} for port in keepstore_ports + }, + }, + "Keepproxy": { + "ExternalURL": "https://%s:%s" % (localhost, keepproxy_external_port), + "InternalURLs": { + "http://%s:%s"%(localhost, keepproxy_port): {}, + }, + }, + "WebDAV": { + "ExternalURL": "https://%s:%s" % (localhost, keep_web_external_port), + "InternalURLs": { + "http://%s:%s"%(localhost, keep_web_port): {}, + }, + }, + "WebDAVDownload": { + "ExternalURL": "https://%s:%s" % (localhost, keep_web_dl_external_port), + "InternalURLs": { + "http://%s:%s"%(localhost, keep_web_dl_port): {}, + }, + }, + "SSO": { + "ExternalURL": "http://localhost:3002", + }, + } + + config = { + "Clusters": { + "zzzzz": { + "ManagementToken": "e687950a23c3a9bceec28c6223a06c79", + "SystemRootToken": auth_token('system_user'), + "API": { + "RequestTimeout": "30s", + "RailsSessionSecretToken": "e24205c490ac07e028fd5f8a692dcb398bcd654eff1aef5f9fe6891994b18483", + }, + "Login": { + "SSO": { + "ProviderAppID": "arvados-server", + "ProviderAppSecret": "608dbf356a327e2d0d4932b60161e212c2d8d8f5e25690d7b622f850a990cd33", + }, + }, + "SystemLogs": { + "LogLevel": ('info' if os.environ.get('ARVADOS_DEBUG', '') in ['','0'] else 'debug'), + }, + "PostgreSQL": { + "Connection": pgconnection, + }, + "TLS": { + "Insecure": True, + }, + "Services": services, + "Users": { + "AnonymousUserToken": auth_token('anonymous'), + "UserProfileNotificationAddress": "arvados@example.com", + }, + "Collections": { + "BlobSigningKey": "zfhgfenhffzltr9dixws36j1yhksjoll2grmku38mi7yxd66h5j4q9w4jzanezacp8s6q0ro3hxakfye02152hncy6zml2ed0uc", + "TrustAllContent": False, + "ForwardSlashNameSubstitution": "/", + "TrashSweepInterval": "-1s", + }, + "Git": { + "Repositories": os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'git', 'test'), + }, + "Containers": { + "JobsAPI": { + "GitInternalDir": os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'internal.git'), + }, + "SupportedDockerImageFormats": {"v1": {}}, + }, + "Volumes": { + "zzzzz-nyw5e-%015d"%n: { + "AccessViaHosts": { + "http://%s:%s" % (localhost, keepstore_ports[n]): {}, + }, + "Driver": "Directory", + "DriverParameters": { + "Root": os.path.join(TEST_TMPDIR, "keep%d.data"%n), + }, + } for n in range(len(keepstore_ports)) + }, + }, + }, + } + + conf = os.path.join(TEST_TMPDIR, 'arvados.yml') + with open(conf, 'w') as f: + yaml.safe_dump(config, f) + + ex = "export ARVADOS_CONFIG="+conf + print(ex) + def stop_nginx(): if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ: @@ -652,44 +830,6 @@ def stop_nginx(): def _pidfile(program): return os.path.join(TEST_TMPDIR, program + '.pid') -def _portfile(program): - return os.path.join(TEST_TMPDIR, program + '.port') - -def _setport(program, port): - with open(_portfile(program), 'w') as f: - f.write(str(port)) - -# Returns 9 if program is not up. -def _getport(program): - try: - return int(open(_portfile(program)).read()) - except IOError: - return 9 - -def _dbconfig(key): - global _cached_db_config - if not _cached_db_config: - _cached_db_config = yaml.load(open(os.path.join( - SERVICES_SRC_DIR, 'api', 'config', 'database.yml'))) - return _cached_db_config['test'][key] - -def _apiconfig(key): - global _cached_config - if _cached_config: - return _cached_config[key] - 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', required=False) - _cached_config = {} - for section in [cdefault.get('common',{}), cdefault.get('test',{}), - csite.get('common',{}), csite.get('test',{})]: - _cached_config.update(section) - return _cached_config[key] - def fixture(fix): '''load a fixture yaml file''' with open(os.path.join(SERVICES_SRC_DIR, 'api', "test", "fixtures", @@ -700,7 +840,7 @@ def fixture(fix): yaml_file = yaml_file[0:trim_index] except ValueError: pass - return yaml.load(yaml_file) + return yaml.safe_load(yaml_file) def auth_token(token_name): return fixture("api_client_authorizations")[token_name]["api_token"] @@ -735,7 +875,7 @@ class TestCaseWithServers(unittest.TestCase): @staticmethod def _restore_dict(src, dest): - for key in dest.keys(): + for key in list(dest.keys()): if key not in src: del dest[key] dest.update(src) @@ -777,17 +917,18 @@ if __name__ == "__main__": actions = [ 'start', 'stop', 'start_ws', 'stop_ws', + 'start_controller', 'stop_controller', 'start_keep', 'stop_keep', 'start_keep_proxy', 'stop_keep_proxy', 'start_keep-web', 'stop_keep-web', 'start_arv-git-httpd', 'stop_arv-git-httpd', - 'start_nginx', 'stop_nginx', + 'start_nginx', 'stop_nginx', 'setup_config', ] parser = argparse.ArgumentParser() parser.add_argument('action', type=str, help="one of {}".format(actions)) parser.add_argument('--auth', type=str, metavar='FIXTURE_NAME', help='Print authorization info for given api_client_authorizations fixture') parser.add_argument('--num-keep-servers', metavar='int', type=int, default=2, help="Number of keep servers desired") - parser.add_argument('--keep-enforce-permissions', action="store_true", help="Enforce keep permissions") + parser.add_argument('--keep-blob-signing', action="store_true", help="Enable blob signing for keepstore servers") args = parser.parse_args() @@ -813,8 +954,12 @@ if __name__ == "__main__": run_ws() elif args.action == 'stop_ws': stop_ws() + elif args.action == 'start_controller': + run_controller() + elif args.action == 'stop_controller': + stop_controller() elif args.action == 'start_keep': - run_keep(enforce_permissions=args.keep_enforce_permissions, num_servers=args.num_keep_servers) + run_keep(blob_signing=args.keep_blob_signing, num_servers=args.num_keep_servers) elif args.action == 'stop_keep': stop_keep(num_servers=args.num_keep_servers) elif args.action == 'start_keep_proxy': @@ -831,7 +976,10 @@ if __name__ == "__main__": stop_keep_web() elif args.action == 'start_nginx': run_nginx() + print("export ARVADOS_API_HOST=0.0.0.0:{}".format(external_port_from_config('Controller'))) elif args.action == 'stop_nginx': stop_nginx() + elif args.action == 'setup_config': + setup_config() else: raise Exception("action recognized but not implemented!?")