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:
420 "arvados-controller":
422 "arvados-api-server":
428 _dbconfig('database'),
429 _dbconfig('username'),
430 _dbconfig('password'),
434 logf = open(_logfilename('controller'), 'a')
435 controller = subprocess.Popen(
436 ["arvados-server", "controller", "-config", conf],
437 stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
438 with open(_pidfile('controller'), 'w') as f:
439 f.write(str(controller.pid))
440 _wait_until_port_listens(port)
441 _setport('controller', port)
444 def stop_controller():
445 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
447 kill_server_pid(_pidfile('controller'))
450 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
453 port = find_available_port()
454 conf = os.path.join(TEST_TMPDIR, 'ws.yml')
455 with open(conf, 'w') as f:
468 """.format(os.environ['ARVADOS_API_HOST'],
470 ('info' if os.environ.get('ARVADOS_DEBUG', '') in ['','0'] else 'debug'),
472 _dbconfig('database'),
473 _dbconfig('username'),
474 _dbconfig('password')))
475 logf = open(_logfilename('ws'), 'a')
476 ws = subprocess.Popen(
477 ["ws", "-config", conf],
478 stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
479 with open(_pidfile('ws'), 'w') as f:
481 _wait_until_port_listens(port)
486 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
488 kill_server_pid(_pidfile('ws'))
490 def _start_keep(n, keep_args):
491 keep0 = tempfile.mkdtemp()
492 port = find_available_port()
493 keep_cmd = ["keepstore",
494 "-volume={}".format(keep0),
495 "-listen=:{}".format(port),
496 "-pid="+_pidfile('keep{}'.format(n))]
498 for arg, val in keep_args.items():
499 keep_cmd.append("{}={}".format(arg, val))
501 logf = open(_logfilename('keep{}'.format(n)), 'a')
502 kp0 = subprocess.Popen(
503 keep_cmd, stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
505 with open(_pidfile('keep{}'.format(n)), 'w') as f:
506 f.write(str(kp0.pid))
508 with open("{}/keep{}.volume".format(TEST_TMPDIR, n), 'w') as f:
511 _wait_until_port_listens(port)
515 def run_keep(blob_signing_key=None, enforce_permissions=False, num_servers=2):
516 stop_keep(num_servers)
519 if not blob_signing_key:
520 blob_signing_key = 'zfhgfenhffzltr9dixws36j1yhksjoll2grmku38mi7yxd66h5j4q9w4jzanezacp8s6q0ro3hxakfye02152hncy6zml2ed0uc'
521 with open(os.path.join(TEST_TMPDIR, "keep.blob_signing_key"), "w") as f:
522 keep_args['-blob-signing-key-file'] = f.name
523 f.write(blob_signing_key)
524 keep_args['-enforce-permissions'] = str(enforce_permissions).lower()
525 with open(os.path.join(TEST_TMPDIR, "keep.data-manager-token-file"), "w") as f:
526 keep_args['-data-manager-token-file'] = f.name
527 f.write(auth_token('data_manager'))
528 keep_args['-never-delete'] = 'false'
532 host=os.environ['ARVADOS_API_HOST'],
533 token=os.environ['ARVADOS_API_TOKEN'],
536 for d in api.keep_services().list(filters=[['service_type','=','disk']]).execute()['items']:
537 api.keep_services().delete(uuid=d['uuid']).execute()
538 for d in api.keep_disks().list().execute()['items']:
539 api.keep_disks().delete(uuid=d['uuid']).execute()
541 for d in range(0, num_servers):
542 port = _start_keep(d, keep_args)
543 svc = api.keep_services().create(body={'keep_service': {
544 'uuid': 'zzzzz-bi6l4-keepdisk{:07d}'.format(d),
545 'service_host': 'localhost',
546 'service_port': port,
547 'service_type': 'disk',
548 'service_ssl_flag': False,
550 api.keep_disks().create(body={
551 'keep_disk': {'keep_service_uuid': svc['uuid'] }
554 # If keepproxy and/or keep-web is running, send SIGHUP to make
555 # them discover the new keepstore services.
556 for svc in ('keepproxy', 'keep-web'):
557 pidfile = _pidfile('keepproxy')
558 if os.path.exists(pidfile):
560 os.kill(int(open(pidfile).read()), signal.SIGHUP)
565 kill_server_pid(_pidfile('keep{}'.format(n)))
566 if os.path.exists("{}/keep{}.volume".format(TEST_TMPDIR, n)):
567 with open("{}/keep{}.volume".format(TEST_TMPDIR, n), 'r') as r:
568 shutil.rmtree(r.read(), True)
569 os.unlink("{}/keep{}.volume".format(TEST_TMPDIR, n))
570 if os.path.exists(os.path.join(TEST_TMPDIR, "keep.blob_signing_key")):
571 os.remove(os.path.join(TEST_TMPDIR, "keep.blob_signing_key"))
573 def stop_keep(num_servers=2):
574 for n in range(0, num_servers):
577 def run_keep_proxy():
578 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
582 port = find_available_port()
583 env = os.environ.copy()
584 env['ARVADOS_API_TOKEN'] = auth_token('anonymous')
585 logf = open(_logfilename('keepproxy'), 'a')
586 kp = subprocess.Popen(
588 '-pid='+_pidfile('keepproxy'),
589 '-listen=:{}'.format(port)],
590 env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
594 host=os.environ['ARVADOS_API_HOST'],
595 token=auth_token('admin'),
597 for d in api.keep_services().list(
598 filters=[['service_type','=','proxy']]).execute()['items']:
599 api.keep_services().delete(uuid=d['uuid']).execute()
600 api.keep_services().create(body={'keep_service': {
601 'service_host': 'localhost',
602 'service_port': port,
603 'service_type': 'proxy',
604 'service_ssl_flag': False,
606 os.environ["ARVADOS_KEEP_SERVICES"] = "http://localhost:{}".format(port)
607 _setport('keepproxy', port)
608 _wait_until_port_listens(port)
610 def stop_keep_proxy():
611 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
613 kill_server_pid(_pidfile('keepproxy'))
615 def run_arv_git_httpd():
616 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
620 gitdir = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'git')
621 gitport = find_available_port()
622 env = os.environ.copy()
623 env.pop('ARVADOS_API_TOKEN', None)
624 logf = open(_logfilename('arv-git-httpd'), 'a')
625 agh = subprocess.Popen(
627 '-repo-root='+gitdir+'/test',
628 '-address=:'+str(gitport)],
629 env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf)
630 with open(_pidfile('arv-git-httpd'), 'w') as f:
631 f.write(str(agh.pid))
632 _setport('arv-git-httpd', gitport)
633 _wait_until_port_listens(gitport)
635 def stop_arv_git_httpd():
636 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
638 kill_server_pid(_pidfile('arv-git-httpd'))
641 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
645 keepwebport = find_available_port()
646 env = os.environ.copy()
647 env['ARVADOS_API_TOKEN'] = auth_token('anonymous')
648 logf = open(_logfilename('keep-web'), 'a')
649 keepweb = subprocess.Popen(
652 '-attachment-only-host=download:'+str(keepwebport),
653 '-listen=:'+str(keepwebport)],
654 env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf)
655 with open(_pidfile('keep-web'), 'w') as f:
656 f.write(str(keepweb.pid))
657 _setport('keep-web', keepwebport)
658 _wait_until_port_listens(keepwebport)
661 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
663 kill_server_pid(_pidfile('keep-web'))
666 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
670 nginxconf['CONTROLLERPORT'] = _getport('controller')
671 nginxconf['CONTROLLERSSLPORT'] = find_available_port()
672 nginxconf['KEEPWEBPORT'] = _getport('keep-web')
673 nginxconf['KEEPWEBDLSSLPORT'] = find_available_port()
674 nginxconf['KEEPWEBSSLPORT'] = find_available_port()
675 nginxconf['KEEPPROXYPORT'] = _getport('keepproxy')
676 nginxconf['KEEPPROXYSSLPORT'] = find_available_port()
677 nginxconf['GITPORT'] = _getport('arv-git-httpd')
678 nginxconf['GITSSLPORT'] = find_available_port()
679 nginxconf['WSPORT'] = _getport('ws')
680 nginxconf['WSSPORT'] = _getport('wss')
681 nginxconf['SSLCERT'] = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.pem')
682 nginxconf['SSLKEY'] = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.key')
683 nginxconf['ACCESSLOG'] = _logfilename('nginx_access')
684 nginxconf['ERRORLOG'] = _logfilename('nginx_error')
685 nginxconf['TMPDIR'] = TEST_TMPDIR
687 conftemplatefile = os.path.join(MY_DIRNAME, 'nginx.conf')
688 conffile = os.path.join(TEST_TMPDIR, 'nginx.conf')
689 with open(conffile, 'w') as f:
692 lambda match: str(nginxconf.get(match.group(1))),
693 open(conftemplatefile).read()))
695 env = os.environ.copy()
696 env['PATH'] = env['PATH']+':/sbin:/usr/sbin:/usr/local/sbin'
698 nginx = subprocess.Popen(
700 '-g', 'error_log stderr info;',
701 '-g', 'pid '+_pidfile('nginx')+';',
703 env=env, stdin=open('/dev/null'), stdout=sys.stderr)
704 _setport('controller-ssl', nginxconf['CONTROLLERSSLPORT'])
705 _setport('keep-web-dl-ssl', nginxconf['KEEPWEBDLSSLPORT'])
706 _setport('keep-web-ssl', nginxconf['KEEPWEBSSLPORT'])
707 _setport('keepproxy-ssl', nginxconf['KEEPPROXYSSLPORT'])
708 _setport('arv-git-httpd-ssl', nginxconf['GITSSLPORT'])
711 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
713 kill_server_pid(_pidfile('nginx'))
715 def _pidfile(program):
716 return os.path.join(TEST_TMPDIR, program + '.pid')
718 def _portfile(program):
719 return os.path.join(TEST_TMPDIR, program + '.port')
721 def _setport(program, port):
722 with open(_portfile(program), 'w') as f:
725 # Returns 9 if program is not up.
726 def _getport(program):
728 return int(open(_portfile(program)).read())
733 global _cached_db_config
734 if not _cached_db_config:
735 _cached_db_config = yaml.load(open(os.path.join(
736 SERVICES_SRC_DIR, 'api', 'config', 'database.yml')))
737 return _cached_db_config['test'][key]
740 global _cached_config
742 return _cached_config[key]
743 def _load(f, required=True):
744 fullpath = os.path.join(SERVICES_SRC_DIR, 'api', 'config', f)
745 if not required and not os.path.exists(fullpath):
747 return yaml.load(fullpath)
748 cdefault = _load('application.default.yml')
749 csite = _load('application.yml', required=False)
751 for section in [cdefault.get('common',{}), cdefault.get('test',{}),
752 csite.get('common',{}), csite.get('test',{})]:
753 _cached_config.update(section)
754 return _cached_config[key]
757 '''load a fixture yaml file'''
758 with open(os.path.join(SERVICES_SRC_DIR, 'api', "test", "fixtures",
762 trim_index = yaml_file.index("# Test Helper trims the rest of the file")
763 yaml_file = yaml_file[0:trim_index]
766 return yaml.load(yaml_file)
768 def auth_token(token_name):
769 return fixture("api_client_authorizations")[token_name]["api_token"]
771 def authorize_with(token_name):
772 '''token_name is the symbolic name of the token from the api_client_authorizations fixture'''
773 arvados.config.settings()["ARVADOS_API_TOKEN"] = auth_token(token_name)
774 arvados.config.settings()["ARVADOS_API_HOST"] = os.environ.get("ARVADOS_API_HOST")
775 arvados.config.settings()["ARVADOS_API_HOST_INSECURE"] = "true"
777 class TestCaseWithServers(unittest.TestCase):
778 """TestCase to start and stop supporting Arvados servers.
780 Define any of MAIN_SERVER, KEEP_SERVER, and/or KEEP_PROXY_SERVER
781 class variables as a dictionary of keyword arguments. If you do,
782 setUpClass will start the corresponding servers by passing these
783 keyword arguments to the run, run_keep, and/or run_keep_server
784 functions, respectively. It will also set Arvados environment
785 variables to point to these servers appropriately. If you don't
786 run a Keep or Keep proxy server, setUpClass will set up a
787 temporary directory for Keep local storage, and set it as
790 tearDownClass will stop any servers started, and restore the
791 original environment.
796 KEEP_PROXY_SERVER = None
797 KEEP_WEB_SERVER = None
800 def _restore_dict(src, dest):
801 for key in list(dest.keys()):
808 cls._orig_environ = os.environ.copy()
809 cls._orig_config = arvados.config.settings().copy()
810 cls._cleanup_funcs = []
811 os.environ.pop('ARVADOS_KEEP_SERVICES', None)
812 os.environ.pop('ARVADOS_EXTERNAL_CLIENT', None)
813 for server_kwargs, start_func, stop_func in (
814 (cls.MAIN_SERVER, run, reset),
815 (cls.WS_SERVER, run_ws, stop_ws),
816 (cls.KEEP_SERVER, run_keep, stop_keep),
817 (cls.KEEP_PROXY_SERVER, run_keep_proxy, stop_keep_proxy),
818 (cls.KEEP_WEB_SERVER, run_keep_web, stop_keep_web)):
819 if server_kwargs is not None:
820 start_func(**server_kwargs)
821 cls._cleanup_funcs.append(stop_func)
822 if (cls.KEEP_SERVER is None) and (cls.KEEP_PROXY_SERVER is None):
823 cls.local_store = tempfile.mkdtemp()
824 os.environ['KEEP_LOCAL_STORE'] = cls.local_store
825 cls._cleanup_funcs.append(
826 lambda: shutil.rmtree(cls.local_store, ignore_errors=True))
828 os.environ.pop('KEEP_LOCAL_STORE', None)
829 arvados.config.initialize()
832 def tearDownClass(cls):
833 for clean_func in cls._cleanup_funcs:
835 cls._restore_dict(cls._orig_environ, os.environ)
836 cls._restore_dict(cls._orig_config, arvados.config.settings())
839 if __name__ == "__main__":
842 'start_ws', 'stop_ws',
843 'start_controller', 'stop_controller',
844 'start_keep', 'stop_keep',
845 'start_keep_proxy', 'stop_keep_proxy',
846 'start_keep-web', 'stop_keep-web',
847 'start_arv-git-httpd', 'stop_arv-git-httpd',
848 'start_nginx', 'stop_nginx',
850 parser = argparse.ArgumentParser()
851 parser.add_argument('action', type=str, help="one of {}".format(actions))
852 parser.add_argument('--auth', type=str, metavar='FIXTURE_NAME', help='Print authorization info for given api_client_authorizations fixture')
853 parser.add_argument('--num-keep-servers', metavar='int', type=int, default=2, help="Number of keep servers desired")
854 parser.add_argument('--keep-enforce-permissions', action="store_true", help="Enforce keep permissions")
856 args = parser.parse_args()
858 if args.action not in actions:
859 print("Unrecognized action '{}'. Actions are: {}.".
860 format(args.action, actions),
863 if args.action == 'start':
864 stop(force=('ARVADOS_TEST_API_HOST' not in os.environ))
865 run(leave_running_atexit=True)
866 host = os.environ['ARVADOS_API_HOST']
867 if args.auth is not None:
868 token = auth_token(args.auth)
869 print("export ARVADOS_API_TOKEN={}".format(pipes.quote(token)))
870 print("export ARVADOS_API_HOST={}".format(pipes.quote(host)))
871 print("export ARVADOS_API_HOST_INSECURE=true")
874 elif args.action == 'stop':
875 stop(force=('ARVADOS_TEST_API_HOST' not in os.environ))
876 elif args.action == 'start_ws':
878 elif args.action == 'stop_ws':
880 elif args.action == 'start_controller':
882 elif args.action == 'stop_controller':
884 elif args.action == 'start_keep':
885 run_keep(enforce_permissions=args.keep_enforce_permissions, num_servers=args.num_keep_servers)
886 elif args.action == 'stop_keep':
887 stop_keep(num_servers=args.num_keep_servers)
888 elif args.action == 'start_keep_proxy':
890 elif args.action == 'stop_keep_proxy':
892 elif args.action == 'start_arv-git-httpd':
894 elif args.action == 'stop_arv-git-httpd':
896 elif args.action == 'start_keep-web':
898 elif args.action == 'stop_keep-web':
900 elif args.action == 'start_nginx':
902 print("export ARVADOS_API_HOST=0.0.0.0:{}".format(_getport('controller-ssl')))
903 elif args.action == 'stop_nginx':
906 raise Exception("action recognized but not implemented!?")