1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: Apache-2.0
5 from __future__ import print_function
6 from __future__ import division
7 from builtins import str
8 from builtins import range
29 MY_DIRNAME = os.path.dirname(os.path.realpath(__file__))
30 if __name__ == '__main__' and os.path.exists(
31 os.path.join(MY_DIRNAME, '..', 'arvados', '__init__.py')):
32 # We're being launched to support another test suite.
33 # Add the Python SDK source to the library path.
34 sys.path.insert(1, os.path.dirname(MY_DIRNAME))
39 ARVADOS_DIR = os.path.realpath(os.path.join(MY_DIRNAME, '../../..'))
40 SERVICES_SRC_DIR = os.path.join(ARVADOS_DIR, 'services')
41 if 'GOPATH' in os.environ:
42 # Add all GOPATH bin dirs to PATH -- but insert them after the
43 # ruby gems bin dir, to ensure "bundle" runs the Ruby bundler
44 # command, not the golang.org/x/tools/cmd/bundle command.
45 gopaths = os.environ['GOPATH'].split(':')
46 addbins = [os.path.join(path, 'bin') for path in gopaths]
48 for path in os.environ['PATH'].split(':'):
50 if os.path.exists(os.path.join(path, 'bundle')):
54 os.environ['PATH'] = ':'.join(newbins)
56 TEST_TMPDIR = os.path.join(ARVADOS_DIR, 'tmp')
57 if not os.path.exists(TEST_TMPDIR):
62 _cached_db_config = {}
64 def find_server_pid(PID_PATH, wait=10):
68 while (not good_pid) and (now <= timeout):
71 with open(PID_PATH, 'r') as f:
72 server_pid = int(f.read())
73 good_pid = (os.kill(server_pid, 0) is None)
74 except EnvironmentError:
83 def kill_server_pid(pidfile, wait=10, passenger_root=False):
84 # Must re-import modules in order to work during atexit
95 # First try to shut down nicely
96 restore_cwd = os.getcwd()
97 os.chdir(passenger_root)
99 'bundle', 'exec', 'passenger', 'stop', '--pid-file', pidfile])
100 os.chdir(restore_cwd)
101 # Use up to half of the +wait+ period waiting for "passenger
102 # stop" to work. If the process hasn't exited by then, start
103 # sending TERM signals.
107 while now <= deadline and server_pid is None:
109 with open(pidfile, 'r') as f:
110 server_pid = int(f.read())
112 # No pidfile = nothing to kill.
114 except ValueError as error:
115 # Pidfile exists, but we can't parse it. Perhaps the
116 # server has created the file but hasn't written its PID
118 print("Parse error reading pidfile {}: {}".format(pidfile, error),
123 while now <= deadline:
125 exited, _ = os.waitpid(server_pid, os.WNOHANG)
127 _remove_pidfile(pidfile)
130 # already exited, or isn't our child process
134 os.kill(server_pid, signal.SIGTERM)
135 print("Sent SIGTERM to {} ({})".format(server_pid, pidfile),
137 except OSError as error:
138 if error.errno == errno.ESRCH:
139 # Thrown by os.getpgid() or os.kill() if the process
140 # does not exist, i.e., our work here is done.
141 _remove_pidfile(pidfile)
147 print("Server PID {} ({}) did not exit, giving up after {}s".
148 format(server_pid, pidfile, wait),
151 def _remove_pidfile(pidfile):
155 if os.path.lexists(pidfile):
158 def find_available_port():
159 """Return an IPv4 port number that is not in use right now.
161 We assume whoever needs to use the returned port is able to reuse
162 a recently used port without waiting for TIME_WAIT (see
163 SO_REUSEADDR / SO_REUSEPORT).
165 Some opportunity for races here, but it's better than choosing
166 something at random and not checking at all. If all of our servers
167 (hey Passenger) knew that listening on port 0 was a thing, the OS
168 would take care of the races, and this wouldn't be needed at all.
171 sock = socket.socket()
172 sock.bind(('0.0.0.0', 0))
173 port = sock.getsockname()[1]
177 def _wait_until_port_listens(port, timeout=10):
178 """Wait for a process to start listening on the given port.
180 If nothing listens on the port within the specified timeout (given
181 in seconds), print a warning on stderr before returning.
184 subprocess.check_output(['which', 'lsof'])
185 except subprocess.CalledProcessError:
186 print("WARNING: No `lsof` -- cannot wait for port to listen. "+
187 "Sleeping 0.5 and hoping for the best.",
191 deadline = time.time() + timeout
192 while time.time() < deadline:
194 subprocess.check_output(
195 ['lsof', '-t', '-i', 'tcp:'+str(port)])
196 except subprocess.CalledProcessError:
201 "WARNING: Nothing is listening on port {} (waited {} seconds).".
202 format(port, timeout),
205 def _logfilename(label):
206 """Set up a labelled log file, and return a path to write logs to.
208 Normally, the returned path is {tmpdir}/{label}.log.
210 In debug mode, logs are also written to stderr, with [label]
211 prepended to each line. The returned path is a FIFO.
213 +label+ should contain only alphanumerics: it is also used as part
214 of the FIFO filename.
217 logfilename = os.path.join(TEST_TMPDIR, label+'.log')
218 if not os.environ.get('ARVADOS_DEBUG', ''):
220 fifo = os.path.join(TEST_TMPDIR, label+'.fifo')
223 except OSError as error:
224 if error.errno != errno.ENOENT:
226 os.mkfifo(fifo, 0o700)
227 stdbuf = ['stdbuf', '-i0', '-oL', '-eL']
228 # open(fifo, 'r') would block waiting for someone to open the fifo
229 # for writing, so we need a separate cat process to open it for
231 cat = subprocess.Popen(
232 stdbuf+['cat', fifo],
233 stdin=open('/dev/null'),
234 stdout=subprocess.PIPE)
235 tee = subprocess.Popen(
236 stdbuf+['tee', '-a', logfilename],
238 stdout=subprocess.PIPE)
240 stdbuf+['sed', '-e', 's/^/['+label+'] /'],
245 def run(leave_running_atexit=False):
246 """Ensure an API server is running, and ARVADOS_API_* env vars have
247 admin credentials for it.
249 If ARVADOS_TEST_API_HOST is set, a parent process has started a
250 test server for us to use: we just need to reset() it using the
253 If a previous call to run() started a new server process, and it
254 is still running, we just need to reset() it to fixture state and
257 If neither of those options work out, we'll really start a new
262 # Delete cached discovery documents.
264 # This will clear cached docs that belong to other processes (like
265 # concurrent test suites) even if they're still running. They should
266 # be able to tolerate that.
267 for fn in glob.glob(os.path.join(
268 str(arvados.http_cache('discovery')),
269 '*,arvados,v1,rest,*')):
272 pid_file = _pidfile('api')
273 pid_file_ok = find_server_pid(pid_file, 0)
275 existing_api_host = os.environ.get('ARVADOS_TEST_API_HOST', my_api_host)
276 if existing_api_host and pid_file_ok:
277 if existing_api_host == my_api_host:
281 # Fall through to shutdown-and-start case.
284 # Server was provided by parent. Can't recover if it's
288 # Before trying to start up our own server, call stop() to avoid
289 # "Phusion Passenger Standalone is already running on PID 12345".
290 # (If we've gotten this far, ARVADOS_TEST_API_HOST isn't set, so
291 # we know the server is ours to kill.)
294 restore_cwd = os.getcwd()
295 api_src_dir = os.path.join(SERVICES_SRC_DIR, 'api')
296 os.chdir(api_src_dir)
298 # Either we haven't started a server of our own yet, or it has
299 # died, or we have lost our credentials, or something else is
300 # preventing us from calling reset(). Start a new one.
302 if not os.path.exists('tmp'):
305 if not os.path.exists('tmp/api'):
306 os.makedirs('tmp/api')
308 if not os.path.exists('tmp/logs'):
309 os.makedirs('tmp/logs')
311 # Install the git repository fixtures.
312 gitdir = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'git')
313 gittarball = os.path.join(SERVICES_SRC_DIR, 'api', 'test', 'test.git.tar')
314 if not os.path.isdir(gitdir):
316 subprocess.check_output(['tar', '-xC', gitdir, '-f', gittarball])
318 # The nginx proxy isn't listening here yet, but we need to choose
319 # the wss:// port now so we can write the API server config file.
320 wss_port = find_available_port()
321 _setport('wss', wss_port)
323 port = find_available_port()
324 env = os.environ.copy()
325 env['RAILS_ENV'] = 'test'
326 env['ARVADOS_TEST_WSS_PORT'] = str(wss_port)
327 env.pop('ARVADOS_WEBSOCKETS', None)
328 env.pop('ARVADOS_TEST_API_HOST', None)
329 env.pop('ARVADOS_API_HOST', None)
330 env.pop('ARVADOS_API_HOST_INSECURE', None)
331 env.pop('ARVADOS_API_TOKEN', None)
332 start_msg = subprocess.check_output(
334 'passenger', 'start', '-d', '-p{}'.format(port),
335 '--pid-file', pid_file,
336 '--log-file', os.path.join(os.getcwd(), 'log/test.log'),
338 '--ssl-certificate', 'tmp/self-signed.pem',
339 '--ssl-certificate-key', 'tmp/self-signed.key'],
342 if not leave_running_atexit:
343 atexit.register(kill_server_pid, pid_file, passenger_root=api_src_dir)
345 match = re.search(r'Accessible via: https://(.*?)/', start_msg)
348 "Passenger did not report endpoint: {}".format(start_msg))
349 my_api_host = match.group(1)
350 os.environ['ARVADOS_API_HOST'] = my_api_host
352 # Make sure the server has written its pid file and started
353 # listening on its TCP port
354 find_server_pid(pid_file)
355 _wait_until_port_listens(port)
358 os.chdir(restore_cwd)
361 """Reset the test server to fixture state.
363 This resets the ARVADOS_TEST_API_HOST provided by a parent process
364 if any, otherwise the server started by run().
366 It also resets ARVADOS_* environment vars to point to the test
367 server with admin credentials.
369 existing_api_host = os.environ.get('ARVADOS_TEST_API_HOST', my_api_host)
370 token = auth_token('admin')
371 httpclient = httplib2.Http(ca_certs=os.path.join(
372 SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.pem'))
374 'https://{}/database/reset'.format(existing_api_host),
376 headers={'Authorization': 'OAuth2 {}'.format(token)})
377 os.environ['ARVADOS_API_HOST_INSECURE'] = 'true'
378 os.environ['ARVADOS_API_HOST'] = existing_api_host
379 os.environ['ARVADOS_API_TOKEN'] = token
381 def stop(force=False):
382 """Stop the API server, if one is running.
384 If force==False, kill it only if we started it ourselves. (This
385 supports the use case where a Python test suite calls run(), but
386 run() just uses the ARVADOS_TEST_API_HOST provided by the parent
387 process, and the test suite cleans up after itself by calling
388 stop(). In this case the test server provided by the parent
389 process should be left alone.)
391 If force==True, kill it even if we didn't start it
392 ourselves. (This supports the use case in __main__, where "run"
393 and "stop" happen in different processes.)
396 if force or my_api_host is not None:
397 kill_server_pid(_pidfile('api'))
400 def run_controller():
401 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
404 rails_api_port = int(string.split(os.environ.get('ARVADOS_TEST_API_HOST', my_api_host), ':')[-1])
405 port = find_available_port()
406 conf = os.path.join(TEST_TMPDIR, 'arvados.yml')
407 with open(conf, 'w') as f:
413 "arvados-controller":
415 "arvados-api-server":
418 """.format(port, rails_api_port))
419 logf = open(_logfilename('controller'), 'a')
420 controller = subprocess.Popen(
421 ["arvados-server", "controller", "-config", conf],
422 stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
423 with open(_pidfile('controller'), 'w') as f:
424 f.write(str(controller.pid))
425 _wait_until_port_listens(port)
426 _setport('controller', port)
429 def stop_controller():
430 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
432 kill_server_pid(_pidfile('controller'))
435 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
438 port = find_available_port()
439 conf = os.path.join(TEST_TMPDIR, 'ws.yml')
440 with open(conf, 'w') as f:
453 """.format(os.environ['ARVADOS_API_HOST'],
455 ('info' if os.environ.get('ARVADOS_DEBUG', '') in ['','0'] else 'debug'),
457 _dbconfig('database'),
458 _dbconfig('username'),
459 _dbconfig('password')))
460 logf = open(_logfilename('ws'), 'a')
461 ws = subprocess.Popen(
462 ["ws", "-config", conf],
463 stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
464 with open(_pidfile('ws'), 'w') as f:
466 _wait_until_port_listens(port)
471 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
473 kill_server_pid(_pidfile('ws'))
475 def _start_keep(n, keep_args):
476 keep0 = tempfile.mkdtemp()
477 port = find_available_port()
478 keep_cmd = ["keepstore",
479 "-volume={}".format(keep0),
480 "-listen=:{}".format(port),
481 "-pid="+_pidfile('keep{}'.format(n))]
483 for arg, val in keep_args.items():
484 keep_cmd.append("{}={}".format(arg, val))
486 logf = open(_logfilename('keep{}'.format(n)), 'a')
487 kp0 = subprocess.Popen(
488 keep_cmd, stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
490 with open(_pidfile('keep{}'.format(n)), 'w') as f:
491 f.write(str(kp0.pid))
493 with open("{}/keep{}.volume".format(TEST_TMPDIR, n), 'w') as f:
496 _wait_until_port_listens(port)
500 def run_keep(blob_signing_key=None, enforce_permissions=False, num_servers=2):
501 stop_keep(num_servers)
504 if not blob_signing_key:
505 blob_signing_key = 'zfhgfenhffzltr9dixws36j1yhksjoll2grmku38mi7yxd66h5j4q9w4jzanezacp8s6q0ro3hxakfye02152hncy6zml2ed0uc'
506 with open(os.path.join(TEST_TMPDIR, "keep.blob_signing_key"), "w") as f:
507 keep_args['-blob-signing-key-file'] = f.name
508 f.write(blob_signing_key)
509 keep_args['-enforce-permissions'] = str(enforce_permissions).lower()
510 with open(os.path.join(TEST_TMPDIR, "keep.data-manager-token-file"), "w") as f:
511 keep_args['-data-manager-token-file'] = f.name
512 f.write(auth_token('data_manager'))
513 keep_args['-never-delete'] = 'false'
517 host=os.environ['ARVADOS_API_HOST'],
518 token=os.environ['ARVADOS_API_TOKEN'],
521 for d in api.keep_services().list(filters=[['service_type','=','disk']]).execute()['items']:
522 api.keep_services().delete(uuid=d['uuid']).execute()
523 for d in api.keep_disks().list().execute()['items']:
524 api.keep_disks().delete(uuid=d['uuid']).execute()
526 for d in range(0, num_servers):
527 port = _start_keep(d, keep_args)
528 svc = api.keep_services().create(body={'keep_service': {
529 'uuid': 'zzzzz-bi6l4-keepdisk{:07d}'.format(d),
530 'service_host': 'localhost',
531 'service_port': port,
532 'service_type': 'disk',
533 'service_ssl_flag': False,
535 api.keep_disks().create(body={
536 'keep_disk': {'keep_service_uuid': svc['uuid'] }
539 # If keepproxy and/or keep-web is running, send SIGHUP to make
540 # them discover the new keepstore services.
541 for svc in ('keepproxy', 'keep-web'):
542 pidfile = _pidfile('keepproxy')
543 if os.path.exists(pidfile):
545 os.kill(int(open(pidfile).read()), signal.SIGHUP)
550 kill_server_pid(_pidfile('keep{}'.format(n)))
551 if os.path.exists("{}/keep{}.volume".format(TEST_TMPDIR, n)):
552 with open("{}/keep{}.volume".format(TEST_TMPDIR, n), 'r') as r:
553 shutil.rmtree(r.read(), True)
554 os.unlink("{}/keep{}.volume".format(TEST_TMPDIR, n))
555 if os.path.exists(os.path.join(TEST_TMPDIR, "keep.blob_signing_key")):
556 os.remove(os.path.join(TEST_TMPDIR, "keep.blob_signing_key"))
558 def stop_keep(num_servers=2):
559 for n in range(0, num_servers):
562 def run_keep_proxy():
563 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
567 port = find_available_port()
568 env = os.environ.copy()
569 env['ARVADOS_API_TOKEN'] = auth_token('anonymous')
570 logf = open(_logfilename('keepproxy'), 'a')
571 kp = subprocess.Popen(
573 '-pid='+_pidfile('keepproxy'),
574 '-listen=:{}'.format(port)],
575 env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
579 host=os.environ['ARVADOS_API_HOST'],
580 token=auth_token('admin'),
582 for d in api.keep_services().list(
583 filters=[['service_type','=','proxy']]).execute()['items']:
584 api.keep_services().delete(uuid=d['uuid']).execute()
585 api.keep_services().create(body={'keep_service': {
586 'service_host': 'localhost',
587 'service_port': port,
588 'service_type': 'proxy',
589 'service_ssl_flag': False,
591 os.environ["ARVADOS_KEEP_SERVICES"] = "http://localhost:{}".format(port)
592 _setport('keepproxy', port)
593 _wait_until_port_listens(port)
595 def stop_keep_proxy():
596 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
598 kill_server_pid(_pidfile('keepproxy'))
600 def run_arv_git_httpd():
601 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
605 gitdir = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'git')
606 gitport = find_available_port()
607 env = os.environ.copy()
608 env.pop('ARVADOS_API_TOKEN', None)
609 logf = open(_logfilename('arv-git-httpd'), 'a')
610 agh = subprocess.Popen(
612 '-repo-root='+gitdir+'/test',
613 '-address=:'+str(gitport)],
614 env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf)
615 with open(_pidfile('arv-git-httpd'), 'w') as f:
616 f.write(str(agh.pid))
617 _setport('arv-git-httpd', gitport)
618 _wait_until_port_listens(gitport)
620 def stop_arv_git_httpd():
621 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
623 kill_server_pid(_pidfile('arv-git-httpd'))
626 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
630 keepwebport = find_available_port()
631 env = os.environ.copy()
632 env['ARVADOS_API_TOKEN'] = auth_token('anonymous')
633 logf = open(_logfilename('keep-web'), 'a')
634 keepweb = subprocess.Popen(
637 '-attachment-only-host=download:'+str(keepwebport),
638 '-listen=:'+str(keepwebport)],
639 env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf)
640 with open(_pidfile('keep-web'), 'w') as f:
641 f.write(str(keepweb.pid))
642 _setport('keep-web', keepwebport)
643 _wait_until_port_listens(keepwebport)
646 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
648 kill_server_pid(_pidfile('keep-web'))
651 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
655 nginxconf['CONTROLLERPORT'] = _getport('controller')
656 nginxconf['CONTROLLERSSLPORT'] = find_available_port()
657 nginxconf['KEEPWEBPORT'] = _getport('keep-web')
658 nginxconf['KEEPWEBDLSSLPORT'] = find_available_port()
659 nginxconf['KEEPWEBSSLPORT'] = find_available_port()
660 nginxconf['KEEPPROXYPORT'] = _getport('keepproxy')
661 nginxconf['KEEPPROXYSSLPORT'] = find_available_port()
662 nginxconf['GITPORT'] = _getport('arv-git-httpd')
663 nginxconf['GITSSLPORT'] = find_available_port()
664 nginxconf['WSPORT'] = _getport('ws')
665 nginxconf['WSSPORT'] = _getport('wss')
666 nginxconf['SSLCERT'] = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.pem')
667 nginxconf['SSLKEY'] = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.key')
668 nginxconf['ACCESSLOG'] = _logfilename('nginx_access')
669 nginxconf['ERRORLOG'] = _logfilename('nginx_error')
670 nginxconf['TMPDIR'] = TEST_TMPDIR
672 conftemplatefile = os.path.join(MY_DIRNAME, 'nginx.conf')
673 conffile = os.path.join(TEST_TMPDIR, 'nginx.conf')
674 with open(conffile, 'w') as f:
677 lambda match: str(nginxconf.get(match.group(1))),
678 open(conftemplatefile).read()))
680 env = os.environ.copy()
681 env['PATH'] = env['PATH']+':/sbin:/usr/sbin:/usr/local/sbin'
683 nginx = subprocess.Popen(
685 '-g', 'error_log stderr info;',
686 '-g', 'pid '+_pidfile('nginx')+';',
688 env=env, stdin=open('/dev/null'), stdout=sys.stderr)
689 _setport('controller-ssl', nginxconf['CONTROLLERSSLPORT'])
690 _setport('keep-web-dl-ssl', nginxconf['KEEPWEBDLSSLPORT'])
691 _setport('keep-web-ssl', nginxconf['KEEPWEBSSLPORT'])
692 _setport('keepproxy-ssl', nginxconf['KEEPPROXYSSLPORT'])
693 _setport('arv-git-httpd-ssl', nginxconf['GITSSLPORT'])
696 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
698 kill_server_pid(_pidfile('nginx'))
700 def _pidfile(program):
701 return os.path.join(TEST_TMPDIR, program + '.pid')
703 def _portfile(program):
704 return os.path.join(TEST_TMPDIR, program + '.port')
706 def _setport(program, port):
707 with open(_portfile(program), 'w') as f:
710 # Returns 9 if program is not up.
711 def _getport(program):
713 return int(open(_portfile(program)).read())
718 global _cached_db_config
719 if not _cached_db_config:
720 _cached_db_config = yaml.load(open(os.path.join(
721 SERVICES_SRC_DIR, 'api', 'config', 'database.yml')))
722 return _cached_db_config['test'][key]
725 global _cached_config
727 return _cached_config[key]
728 def _load(f, required=True):
729 fullpath = os.path.join(SERVICES_SRC_DIR, 'api', 'config', f)
730 if not required and not os.path.exists(fullpath):
732 return yaml.load(fullpath)
733 cdefault = _load('application.default.yml')
734 csite = _load('application.yml', required=False)
736 for section in [cdefault.get('common',{}), cdefault.get('test',{}),
737 csite.get('common',{}), csite.get('test',{})]:
738 _cached_config.update(section)
739 return _cached_config[key]
742 '''load a fixture yaml file'''
743 with open(os.path.join(SERVICES_SRC_DIR, 'api', "test", "fixtures",
747 trim_index = yaml_file.index("# Test Helper trims the rest of the file")
748 yaml_file = yaml_file[0:trim_index]
751 return yaml.load(yaml_file)
753 def auth_token(token_name):
754 return fixture("api_client_authorizations")[token_name]["api_token"]
756 def authorize_with(token_name):
757 '''token_name is the symbolic name of the token from the api_client_authorizations fixture'''
758 arvados.config.settings()["ARVADOS_API_TOKEN"] = auth_token(token_name)
759 arvados.config.settings()["ARVADOS_API_HOST"] = os.environ.get("ARVADOS_API_HOST")
760 arvados.config.settings()["ARVADOS_API_HOST_INSECURE"] = "true"
762 class TestCaseWithServers(unittest.TestCase):
763 """TestCase to start and stop supporting Arvados servers.
765 Define any of MAIN_SERVER, KEEP_SERVER, and/or KEEP_PROXY_SERVER
766 class variables as a dictionary of keyword arguments. If you do,
767 setUpClass will start the corresponding servers by passing these
768 keyword arguments to the run, run_keep, and/or run_keep_server
769 functions, respectively. It will also set Arvados environment
770 variables to point to these servers appropriately. If you don't
771 run a Keep or Keep proxy server, setUpClass will set up a
772 temporary directory for Keep local storage, and set it as
775 tearDownClass will stop any servers started, and restore the
776 original environment.
781 KEEP_PROXY_SERVER = None
782 KEEP_WEB_SERVER = None
785 def _restore_dict(src, dest):
786 for key in list(dest.keys()):
793 cls._orig_environ = os.environ.copy()
794 cls._orig_config = arvados.config.settings().copy()
795 cls._cleanup_funcs = []
796 os.environ.pop('ARVADOS_KEEP_SERVICES', None)
797 os.environ.pop('ARVADOS_EXTERNAL_CLIENT', None)
798 for server_kwargs, start_func, stop_func in (
799 (cls.MAIN_SERVER, run, reset),
800 (cls.WS_SERVER, run_ws, stop_ws),
801 (cls.KEEP_SERVER, run_keep, stop_keep),
802 (cls.KEEP_PROXY_SERVER, run_keep_proxy, stop_keep_proxy),
803 (cls.KEEP_WEB_SERVER, run_keep_web, stop_keep_web)):
804 if server_kwargs is not None:
805 start_func(**server_kwargs)
806 cls._cleanup_funcs.append(stop_func)
807 if (cls.KEEP_SERVER is None) and (cls.KEEP_PROXY_SERVER is None):
808 cls.local_store = tempfile.mkdtemp()
809 os.environ['KEEP_LOCAL_STORE'] = cls.local_store
810 cls._cleanup_funcs.append(
811 lambda: shutil.rmtree(cls.local_store, ignore_errors=True))
813 os.environ.pop('KEEP_LOCAL_STORE', None)
814 arvados.config.initialize()
817 def tearDownClass(cls):
818 for clean_func in cls._cleanup_funcs:
820 cls._restore_dict(cls._orig_environ, os.environ)
821 cls._restore_dict(cls._orig_config, arvados.config.settings())
824 if __name__ == "__main__":
827 'start_ws', 'stop_ws',
828 'start_controller', 'stop_controller',
829 'start_keep', 'stop_keep',
830 'start_keep_proxy', 'stop_keep_proxy',
831 'start_keep-web', 'stop_keep-web',
832 'start_arv-git-httpd', 'stop_arv-git-httpd',
833 'start_nginx', 'stop_nginx',
835 parser = argparse.ArgumentParser()
836 parser.add_argument('action', type=str, help="one of {}".format(actions))
837 parser.add_argument('--auth', type=str, metavar='FIXTURE_NAME', help='Print authorization info for given api_client_authorizations fixture')
838 parser.add_argument('--num-keep-servers', metavar='int', type=int, default=2, help="Number of keep servers desired")
839 parser.add_argument('--keep-enforce-permissions', action="store_true", help="Enforce keep permissions")
841 args = parser.parse_args()
843 if args.action not in actions:
844 print("Unrecognized action '{}'. Actions are: {}.".
845 format(args.action, actions),
848 if args.action == 'start':
849 stop(force=('ARVADOS_TEST_API_HOST' not in os.environ))
850 run(leave_running_atexit=True)
851 host = os.environ['ARVADOS_API_HOST']
852 if args.auth is not None:
853 token = auth_token(args.auth)
854 print("export ARVADOS_API_TOKEN={}".format(pipes.quote(token)))
855 print("export ARVADOS_API_HOST={}".format(pipes.quote(host)))
856 print("export ARVADOS_API_HOST_INSECURE=true")
859 elif args.action == 'stop':
860 stop(force=('ARVADOS_TEST_API_HOST' not in os.environ))
861 elif args.action == 'start_ws':
863 elif args.action == 'stop_ws':
865 elif args.action == 'start_controller':
867 elif args.action == 'stop_controller':
869 elif args.action == 'start_keep':
870 run_keep(enforce_permissions=args.keep_enforce_permissions, num_servers=args.num_keep_servers)
871 elif args.action == 'stop_keep':
872 stop_keep(num_servers=args.num_keep_servers)
873 elif args.action == 'start_keep_proxy':
875 elif args.action == 'stop_keep_proxy':
877 elif args.action == 'start_arv-git-httpd':
879 elif args.action == 'stop_arv-git-httpd':
881 elif args.action == 'start_keep-web':
883 elif args.action == 'stop_keep-web':
885 elif args.action == 'start_nginx':
887 print("export ARVADOS_API_HOST=0.0.0.0:{}".format(_getport('controller-ssl')))
888 elif args.action == 'stop_nginx':
891 raise Exception("action recognized but not implemented!?")