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
30 from urllib.parse import urlparse
32 from urlparse import urlparse
34 MY_DIRNAME = os.path.dirname(os.path.realpath(__file__))
35 if __name__ == '__main__' and os.path.exists(
36 os.path.join(MY_DIRNAME, '..', 'arvados', '__init__.py')):
37 # We're being launched to support another test suite.
38 # Add the Python SDK source to the library path.
39 sys.path.insert(1, os.path.dirname(MY_DIRNAME))
44 ARVADOS_DIR = os.path.realpath(os.path.join(MY_DIRNAME, '../../..'))
45 SERVICES_SRC_DIR = os.path.join(ARVADOS_DIR, 'services')
46 if 'GOPATH' in os.environ:
47 # Add all GOPATH bin dirs to PATH -- but insert them after the
48 # ruby gems bin dir, to ensure "bundle" runs the Ruby bundler
49 # command, not the golang.org/x/tools/cmd/bundle command.
50 gopaths = os.environ['GOPATH'].split(':')
51 addbins = [os.path.join(path, 'bin') for path in gopaths]
53 for path in os.environ['PATH'].split(':'):
55 if os.path.exists(os.path.join(path, 'bundle')):
59 os.environ['PATH'] = ':'.join(newbins)
61 TEST_TMPDIR = os.path.join(ARVADOS_DIR, 'tmp')
62 if not os.path.exists(TEST_TMPDIR):
67 _cached_db_config = {}
69 def find_server_pid(PID_PATH, wait=10):
73 while (not good_pid) and (now <= timeout):
76 with open(PID_PATH, 'r') as f:
77 server_pid = int(f.read())
78 good_pid = (os.kill(server_pid, 0) is None)
79 except EnvironmentError:
88 def kill_server_pid(pidfile, wait=10, passenger_root=False):
89 # Must re-import modules in order to work during atexit
100 # First try to shut down nicely
101 restore_cwd = os.getcwd()
102 os.chdir(passenger_root)
104 'bundle', 'exec', 'passenger', 'stop', '--pid-file', pidfile])
105 os.chdir(restore_cwd)
106 # Use up to half of the +wait+ period waiting for "passenger
107 # stop" to work. If the process hasn't exited by then, start
108 # sending TERM signals.
112 while now <= deadline and server_pid is None:
114 with open(pidfile, 'r') as f:
115 server_pid = int(f.read())
117 # No pidfile = nothing to kill.
119 except ValueError as error:
120 # Pidfile exists, but we can't parse it. Perhaps the
121 # server has created the file but hasn't written its PID
123 print("Parse error reading pidfile {}: {}".format(pidfile, error),
128 while now <= deadline:
130 exited, _ = os.waitpid(server_pid, os.WNOHANG)
132 _remove_pidfile(pidfile)
135 # already exited, or isn't our child process
139 os.kill(server_pid, signal.SIGTERM)
140 print("Sent SIGTERM to {} ({})".format(server_pid, pidfile),
142 except OSError as error:
143 if error.errno == errno.ESRCH:
144 # Thrown by os.getpgid() or os.kill() if the process
145 # does not exist, i.e., our work here is done.
146 _remove_pidfile(pidfile)
152 print("Server PID {} ({}) did not exit, giving up after {}s".
153 format(server_pid, pidfile, wait),
156 def _remove_pidfile(pidfile):
160 if os.path.lexists(pidfile):
163 def find_available_port():
164 """Return an IPv4 port number that is not in use right now.
166 We assume whoever needs to use the returned port is able to reuse
167 a recently used port without waiting for TIME_WAIT (see
168 SO_REUSEADDR / SO_REUSEPORT).
170 Some opportunity for races here, but it's better than choosing
171 something at random and not checking at all. If all of our servers
172 (hey Passenger) knew that listening on port 0 was a thing, the OS
173 would take care of the races, and this wouldn't be needed at all.
176 sock = socket.socket()
177 sock.bind(('0.0.0.0', 0))
178 port = sock.getsockname()[1]
182 def _wait_until_port_listens(port, timeout=10, warn=True):
183 """Wait for a process to start listening on the given port.
185 If nothing listens on the port within the specified timeout (given
186 in seconds), print a warning on stderr before returning.
189 subprocess.check_output(['which', 'netstat'])
190 except subprocess.CalledProcessError:
191 print("WARNING: No `netstat` -- cannot wait for port to listen. "+
192 "Sleeping 0.5 and hoping for the best.",
196 deadline = time.time() + timeout
197 while time.time() < deadline:
198 if re.search(r'\ntcp.*:'+str(port)+' .* LISTEN *\n', subprocess.check_output(['netstat', '-Wln']).decode()):
203 "WARNING: Nothing is listening on port {} (waited {} seconds).".
204 format(port, timeout),
208 def _logfilename(label):
209 """Set up a labelled log file, and return a path to write logs to.
211 Normally, the returned path is {tmpdir}/{label}.log.
213 In debug mode, logs are also written to stderr, with [label]
214 prepended to each line. The returned path is a FIFO.
216 +label+ should contain only alphanumerics: it is also used as part
217 of the FIFO filename.
220 logfilename = os.path.join(TEST_TMPDIR, label+'.log')
221 if not os.environ.get('ARVADOS_DEBUG', ''):
223 fifo = os.path.join(TEST_TMPDIR, label+'.fifo')
226 except OSError as error:
227 if error.errno != errno.ENOENT:
229 os.mkfifo(fifo, 0o700)
230 stdbuf = ['stdbuf', '-i0', '-oL', '-eL']
231 # open(fifo, 'r') would block waiting for someone to open the fifo
232 # for writing, so we need a separate cat process to open it for
234 cat = subprocess.Popen(
235 stdbuf+['cat', fifo],
236 stdin=open('/dev/null'),
237 stdout=subprocess.PIPE)
238 tee = subprocess.Popen(
239 stdbuf+['tee', '-a', logfilename],
241 stdout=subprocess.PIPE)
243 stdbuf+['sed', '-e', 's/^/['+label+'] /'],
248 def run(leave_running_atexit=False):
249 """Ensure an API server is running, and ARVADOS_API_* env vars have
250 admin credentials for it.
252 If ARVADOS_TEST_API_HOST is set, a parent process has started a
253 test server for us to use: we just need to reset() it using the
256 If a previous call to run() started a new server process, and it
257 is still running, we just need to reset() it to fixture state and
260 If neither of those options work out, we'll really start a new
265 # Delete cached discovery documents.
267 # This will clear cached docs that belong to other processes (like
268 # concurrent test suites) even if they're still running. They should
269 # be able to tolerate that.
270 for fn in glob.glob(os.path.join(
271 str(arvados.http_cache('discovery')),
272 '*,arvados,v1,rest,*')):
275 pid_file = _pidfile('api')
276 pid_file_ok = find_server_pid(pid_file, 0)
278 existing_api_host = os.environ.get('ARVADOS_TEST_API_HOST', my_api_host)
279 if existing_api_host and pid_file_ok:
280 if existing_api_host == my_api_host:
284 # Fall through to shutdown-and-start case.
287 # Server was provided by parent. Can't recover if it's
291 # Before trying to start up our own server, call stop() to avoid
292 # "Phusion Passenger Standalone is already running on PID 12345".
293 # (If we've gotten this far, ARVADOS_TEST_API_HOST isn't set, so
294 # we know the server is ours to kill.)
297 restore_cwd = os.getcwd()
298 api_src_dir = os.path.join(SERVICES_SRC_DIR, 'api')
299 os.chdir(api_src_dir)
301 # Either we haven't started a server of our own yet, or it has
302 # died, or we have lost our credentials, or something else is
303 # preventing us from calling reset(). Start a new one.
305 if not os.path.exists('tmp'):
308 if not os.path.exists('tmp/api'):
309 os.makedirs('tmp/api')
311 if not os.path.exists('tmp/logs'):
312 os.makedirs('tmp/logs')
314 # Install the git repository fixtures.
315 gitdir = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'git')
316 gittarball = os.path.join(SERVICES_SRC_DIR, 'api', 'test', 'test.git.tar')
317 if not os.path.isdir(gitdir):
319 subprocess.check_output(['tar', '-xC', gitdir, '-f', gittarball])
321 port = internal_port_from_config("RailsAPI")
322 env = os.environ.copy()
323 env['RAILS_ENV'] = 'test'
324 env.pop('ARVADOS_WEBSOCKETS', None)
325 env.pop('ARVADOS_TEST_API_HOST', None)
326 env.pop('ARVADOS_API_HOST', None)
327 env.pop('ARVADOS_API_HOST_INSECURE', None)
328 env.pop('ARVADOS_API_TOKEN', None)
329 logf = open(_logfilename('railsapi'), 'a')
330 railsapi = subprocess.Popen(
332 'passenger', 'start', '-p{}'.format(port),
333 '--pid-file', pid_file,
334 '--log-file', '/dev/stdout',
336 '--ssl-certificate', 'tmp/self-signed.pem',
337 '--ssl-certificate-key', 'tmp/self-signed.key'],
338 env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf)
340 if not leave_running_atexit:
341 atexit.register(kill_server_pid, pid_file, passenger_root=api_src_dir)
343 my_api_host = "127.0.0.1:"+str(port)
344 os.environ['ARVADOS_API_HOST'] = my_api_host
346 # Make sure the server has written its pid file and started
347 # listening on its TCP port
348 find_server_pid(pid_file)
349 _wait_until_port_listens(port)
352 os.chdir(restore_cwd)
355 """Reset the test server to fixture state.
357 This resets the ARVADOS_TEST_API_HOST provided by a parent process
358 if any, otherwise the server started by run().
360 It also resets ARVADOS_* environment vars to point to the test
361 server with admin credentials.
363 existing_api_host = os.environ.get('ARVADOS_TEST_API_HOST', my_api_host)
364 token = auth_token('admin')
365 httpclient = httplib2.Http(ca_certs=os.path.join(
366 SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.pem'))
368 'https://{}/database/reset'.format(existing_api_host),
370 headers={'Authorization': 'OAuth2 {}'.format(token), 'Connection':'close'})
372 os.environ['ARVADOS_API_HOST_INSECURE'] = 'true'
373 os.environ['ARVADOS_API_TOKEN'] = token
374 os.environ['ARVADOS_API_HOST'] = existing_api_host
376 def stop(force=False):
377 """Stop the API server, if one is running.
379 If force==False, kill it only if we started it ourselves. (This
380 supports the use case where a Python test suite calls run(), but
381 run() just uses the ARVADOS_TEST_API_HOST provided by the parent
382 process, and the test suite cleans up after itself by calling
383 stop(). In this case the test server provided by the parent
384 process should be left alone.)
386 If force==True, kill it even if we didn't start it
387 ourselves. (This supports the use case in __main__, where "run"
388 and "stop" happen in different processes.)
391 if force or my_api_host is not None:
392 kill_server_pid(_pidfile('api'))
396 with open(os.environ["ARVADOS_CONFIG"]) as f:
397 return yaml.safe_load(f)
399 def internal_port_from_config(service, idx=0):
401 sorted(list(get_config()["Clusters"]["zzzzz"]["Services"][service]["InternalURLs"].keys()))[idx]).
402 netloc.split(":")[1])
404 def external_port_from_config(service):
405 return int(urlparse(get_config()["Clusters"]["zzzzz"]["Services"][service]["ExternalURL"]).netloc.split(":")[1])
407 def run_controller():
408 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
411 logf = open(_logfilename('controller'), 'a')
412 port = internal_port_from_config("Controller")
413 controller = subprocess.Popen(
414 ["arvados-server", "controller"],
415 stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
416 with open(_pidfile('controller'), 'w') as f:
417 f.write(str(controller.pid))
418 _wait_until_port_listens(port)
421 def stop_controller():
422 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
424 kill_server_pid(_pidfile('controller'))
427 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
430 port = internal_port_from_config("Websocket")
431 logf = open(_logfilename('ws'), 'a')
432 ws = subprocess.Popen(["ws"],
433 stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
434 with open(_pidfile('ws'), 'w') as f:
436 _wait_until_port_listens(port)
440 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
442 kill_server_pid(_pidfile('ws'))
444 def _start_keep(n, blob_signing=False):
445 datadir = os.path.join(TEST_TMPDIR, "keep%d.data"%n)
446 if os.path.exists(datadir):
447 shutil.rmtree(datadir)
449 port = internal_port_from_config("Keepstore", idx=n)
451 # Currently, if there are multiple InternalURLs for a single host,
452 # the only way to tell a keepstore process which one it's supposed
453 # to listen on is to supply a redacted version of the config, with
454 # the other InternalURLs removed.
455 conf = os.path.join(TEST_TMPDIR, "keep%d.yaml"%n)
456 confdata = get_config()
457 confdata['Clusters']['zzzzz']['Services']['Keepstore']['InternalURLs'] = {"http://127.0.0.1:%d"%port: {}}
458 confdata['Clusters']['zzzzz']['Collections']['BlobSigning'] = blob_signing
459 with open(conf, 'w') as f:
460 yaml.safe_dump(confdata, f)
461 keep_cmd = ["keepstore", "-config", conf]
463 with open(_logfilename('keep{}'.format(n)), 'a') as logf:
464 with open('/dev/null') as _stdin:
465 child = subprocess.Popen(
466 keep_cmd, stdin=_stdin, stdout=logf, stderr=logf, close_fds=True)
468 print('child.pid is %d'%child.pid, file=sys.stderr)
469 with open(_pidfile('keep{}'.format(n)), 'w') as f:
470 f.write(str(child.pid))
472 _wait_until_port_listens(port)
476 def run_keep(num_servers=2, **kwargs):
477 stop_keep(num_servers)
481 host=os.environ['ARVADOS_API_HOST'],
482 token=os.environ['ARVADOS_API_TOKEN'],
485 for d in api.keep_services().list(filters=[['service_type','=','disk']]).execute()['items']:
486 api.keep_services().delete(uuid=d['uuid']).execute()
487 for d in api.keep_disks().list().execute()['items']:
488 api.keep_disks().delete(uuid=d['uuid']).execute()
490 for d in range(0, num_servers):
491 port = _start_keep(d, **kwargs)
492 svc = api.keep_services().create(body={'keep_service': {
493 'uuid': 'zzzzz-bi6l4-keepdisk{:07d}'.format(d),
494 'service_host': 'localhost',
495 'service_port': port,
496 'service_type': 'disk',
497 'service_ssl_flag': False,
499 api.keep_disks().create(body={
500 'keep_disk': {'keep_service_uuid': svc['uuid'] }
503 # If keepproxy and/or keep-web is running, send SIGHUP to make
504 # them discover the new keepstore services.
505 for svc in ('keepproxy', 'keep-web'):
506 pidfile = _pidfile('keepproxy')
507 if os.path.exists(pidfile):
509 with open(pidfile) as pid:
510 os.kill(int(pid.read()), signal.SIGHUP)
515 kill_server_pid(_pidfile('keep{}'.format(n)))
517 def stop_keep(num_servers=2):
518 for n in range(0, num_servers):
521 def run_keep_proxy():
522 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
523 os.environ["ARVADOS_KEEP_SERVICES"] = "http://localhost:{}".format(internal_port_from_config('Keepproxy'))
527 port = internal_port_from_config("Keepproxy")
528 env = os.environ.copy()
529 env['ARVADOS_API_TOKEN'] = auth_token('anonymous')
530 logf = open(_logfilename('keepproxy'), 'a')
531 kp = subprocess.Popen(
532 ['keepproxy'], env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
534 with open(_pidfile('keepproxy'), 'w') as f:
536 _wait_until_port_listens(port)
538 print("Using API %s token %s" % (os.environ['ARVADOS_API_HOST'], auth_token('admin')), file=sys.stdout)
541 host=os.environ['ARVADOS_API_HOST'],
542 token=auth_token('admin'),
544 for d in api.keep_services().list(
545 filters=[['service_type','=','proxy']]).execute()['items']:
546 api.keep_services().delete(uuid=d['uuid']).execute()
547 api.keep_services().create(body={'keep_service': {
548 'service_host': 'localhost',
549 'service_port': port,
550 'service_type': 'proxy',
551 'service_ssl_flag': False,
553 os.environ["ARVADOS_KEEP_SERVICES"] = "http://localhost:{}".format(port)
554 _wait_until_port_listens(port)
556 def stop_keep_proxy():
557 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
559 kill_server_pid(_pidfile('keepproxy'))
561 def run_arv_git_httpd():
562 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
566 gitport = internal_port_from_config("GitHTTP")
567 env = os.environ.copy()
568 env.pop('ARVADOS_API_TOKEN', None)
569 logf = open(_logfilename('arv-git-httpd'), 'a')
570 agh = subprocess.Popen(['arv-git-httpd'],
571 env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf)
572 with open(_pidfile('arv-git-httpd'), 'w') as f:
573 f.write(str(agh.pid))
574 _wait_until_port_listens(gitport)
576 def stop_arv_git_httpd():
577 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
579 kill_server_pid(_pidfile('arv-git-httpd'))
582 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
586 keepwebport = internal_port_from_config("WebDAV")
587 env = os.environ.copy()
588 logf = open(_logfilename('keep-web'), 'a')
589 keepweb = subprocess.Popen(
591 env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf)
592 with open(_pidfile('keep-web'), 'w') as f:
593 f.write(str(keepweb.pid))
594 _wait_until_port_listens(keepwebport)
597 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
599 kill_server_pid(_pidfile('keep-web'))
602 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
606 nginxconf['LISTENHOST'] = 'localhost'
607 nginxconf['CONTROLLERPORT'] = internal_port_from_config("Controller")
608 nginxconf['CONTROLLERSSLPORT'] = external_port_from_config("Controller")
609 nginxconf['KEEPWEBPORT'] = internal_port_from_config("WebDAV")
610 nginxconf['KEEPWEBDLSSLPORT'] = external_port_from_config("WebDAVDownload")
611 nginxconf['KEEPWEBSSLPORT'] = external_port_from_config("WebDAV")
612 nginxconf['KEEPPROXYPORT'] = internal_port_from_config("Keepproxy")
613 nginxconf['KEEPPROXYSSLPORT'] = external_port_from_config("Keepproxy")
614 nginxconf['GITPORT'] = internal_port_from_config("GitHTTP")
615 nginxconf['GITSSLPORT'] = external_port_from_config("GitHTTP")
616 nginxconf['WSPORT'] = internal_port_from_config("Websocket")
617 nginxconf['WSSSLPORT'] = external_port_from_config("Websocket")
618 nginxconf['WORKBENCH1PORT'] = internal_port_from_config("Workbench1")
619 nginxconf['WORKBENCH1SSLPORT'] = external_port_from_config("Workbench1")
620 nginxconf['SSLCERT'] = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.pem')
621 nginxconf['SSLKEY'] = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.key')
622 nginxconf['ACCESSLOG'] = _logfilename('nginx_access')
623 nginxconf['ERRORLOG'] = _logfilename('nginx_error')
624 nginxconf['TMPDIR'] = TEST_TMPDIR
626 conftemplatefile = os.path.join(MY_DIRNAME, 'nginx.conf')
627 conffile = os.path.join(TEST_TMPDIR, 'nginx.conf')
628 with open(conffile, 'w') as f:
630 r'{{([A-Z]+[A-Z0-9]+)}}',
631 lambda match: str(nginxconf.get(match.group(1))),
632 open(conftemplatefile).read()))
634 env = os.environ.copy()
635 env['PATH'] = env['PATH']+':/sbin:/usr/sbin:/usr/local/sbin'
637 nginx = subprocess.Popen(
639 '-g', 'error_log stderr info;',
640 '-g', 'pid '+_pidfile('nginx')+';',
642 env=env, stdin=open('/dev/null'), stdout=sys.stderr)
643 _wait_until_port_listens(nginxconf['CONTROLLERSSLPORT'])
646 rails_api_port = find_available_port()
647 controller_port = find_available_port()
648 controller_external_port = find_available_port()
649 websocket_port = find_available_port()
650 websocket_external_port = find_available_port()
651 workbench1_port = find_available_port()
652 workbench1_external_port = find_available_port()
653 git_httpd_port = find_available_port()
654 git_httpd_external_port = find_available_port()
655 keepproxy_port = find_available_port()
656 keepproxy_external_port = find_available_port()
657 keepstore_ports = sorted([str(find_available_port()) for _ in xrange(0,4)])
658 keep_web_port = find_available_port()
659 keep_web_external_port = find_available_port()
660 keep_web_dl_port = find_available_port()
661 keep_web_dl_external_port = find_available_port()
663 dbconf = os.path.join(os.environ["CONFIGSRC"], "config.yml")
665 print("Getting config from %s" % dbconf, file=sys.stderr)
667 pgconnection = yaml.safe_load(open(dbconf))["Clusters"]["zzzzz"]["PostgreSQL"]["Connection"]
669 localhost = "127.0.0.1"
673 "https://%s:%s"%(localhost, rails_api_port): {},
677 "ExternalURL": "https://%s:%s" % (localhost, controller_external_port),
679 "http://%s:%s"%(localhost, controller_port): {},
683 "ExternalURL": "wss://%s:%s/websocket" % (localhost, websocket_external_port),
685 "http://%s:%s"%(localhost, websocket_port): {},
689 "ExternalURL": "https://%s:%s/" % (localhost, workbench1_external_port),
691 "http://%s:%s"%(localhost, workbench1_port): {},
695 "ExternalURL": "https://%s:%s" % (localhost, git_httpd_external_port),
697 "http://%s:%s"%(localhost, git_httpd_port): {}
702 "http://%s:%s"%(localhost, port): {} for port in keepstore_ports
706 "ExternalURL": "https://%s:%s" % (localhost, keepproxy_external_port),
708 "http://%s:%s"%(localhost, keepproxy_port): {},
712 "ExternalURL": "https://%s:%s" % (localhost, keep_web_external_port),
714 "http://%s:%s"%(localhost, keep_web_port): {},
718 "ExternalURL": "https://%s:%s" % (localhost, keep_web_dl_external_port),
720 "http://%s:%s"%(localhost, keep_web_dl_port): {},
724 "ExternalURL": "http://localhost:3002",
731 "ManagementToken": "e687950a23c3a9bceec28c6223a06c79",
732 "SystemRootToken": auth_token('system_user'),
734 "RequestTimeout": "30s",
735 "RailsSessionSecretToken": "e24205c490ac07e028fd5f8a692dcb398bcd654eff1aef5f9fe6891994b18483",
738 "ProviderAppID": "arvados-server",
739 "ProviderAppSecret": "608dbf356a327e2d0d4932b60161e212c2d8d8f5e25690d7b622f850a990cd33",
742 "LogLevel": ('info' if os.environ.get('ARVADOS_DEBUG', '') in ['','0'] else 'debug'),
745 "Connection": pgconnection,
750 "Services": services,
752 "AnonymousUserToken": auth_token('anonymous'),
753 "UserProfileNotificationAddress": "arvados@example.com",
756 "BlobSigningKey": "zfhgfenhffzltr9dixws36j1yhksjoll2grmku38mi7yxd66h5j4q9w4jzanezacp8s6q0ro3hxakfye02152hncy6zml2ed0uc",
757 "TrustAllContent": True,
758 "ForwardSlashNameSubstitution": "/",
759 "TrashSweepInterval": "-1s",
762 "Repositories": os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'git', 'test'),
766 "GitInternalDir": os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'internal.git'),
768 "SupportedDockerImageFormats": {"v1": {}},
771 "zzzzz-nyw5e-%015d"%n: {
773 "http://%s:%s" % (localhost, keepstore_ports[n]): {},
775 "Driver": "Directory",
776 "DriverParameters": {
777 "Root": os.path.join(TEST_TMPDIR, "keep%d.data"%n),
779 } for n in range(len(keepstore_ports))
785 conf = os.path.join(TEST_TMPDIR, 'arvados.yml')
786 with open(conf, 'w') as f:
787 yaml.safe_dump(config, f)
789 ex = "export ARVADOS_CONFIG="+conf
794 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
796 kill_server_pid(_pidfile('nginx'))
798 def _pidfile(program):
799 return os.path.join(TEST_TMPDIR, program + '.pid')
802 '''load a fixture yaml file'''
803 with open(os.path.join(SERVICES_SRC_DIR, 'api', "test", "fixtures",
807 trim_index = yaml_file.index("# Test Helper trims the rest of the file")
808 yaml_file = yaml_file[0:trim_index]
811 return yaml.safe_load(yaml_file)
813 def auth_token(token_name):
814 return fixture("api_client_authorizations")[token_name]["api_token"]
816 def authorize_with(token_name):
817 '''token_name is the symbolic name of the token from the api_client_authorizations fixture'''
818 arvados.config.settings()["ARVADOS_API_TOKEN"] = auth_token(token_name)
819 arvados.config.settings()["ARVADOS_API_HOST"] = os.environ.get("ARVADOS_API_HOST")
820 arvados.config.settings()["ARVADOS_API_HOST_INSECURE"] = "true"
822 class TestCaseWithServers(unittest.TestCase):
823 """TestCase to start and stop supporting Arvados servers.
825 Define any of MAIN_SERVER, KEEP_SERVER, and/or KEEP_PROXY_SERVER
826 class variables as a dictionary of keyword arguments. If you do,
827 setUpClass will start the corresponding servers by passing these
828 keyword arguments to the run, run_keep, and/or run_keep_server
829 functions, respectively. It will also set Arvados environment
830 variables to point to these servers appropriately. If you don't
831 run a Keep or Keep proxy server, setUpClass will set up a
832 temporary directory for Keep local storage, and set it as
835 tearDownClass will stop any servers started, and restore the
836 original environment.
841 KEEP_PROXY_SERVER = None
842 KEEP_WEB_SERVER = None
845 def _restore_dict(src, dest):
846 for key in list(dest.keys()):
853 cls._orig_environ = os.environ.copy()
854 cls._orig_config = arvados.config.settings().copy()
855 cls._cleanup_funcs = []
856 os.environ.pop('ARVADOS_KEEP_SERVICES', None)
857 os.environ.pop('ARVADOS_EXTERNAL_CLIENT', None)
858 for server_kwargs, start_func, stop_func in (
859 (cls.MAIN_SERVER, run, reset),
860 (cls.WS_SERVER, run_ws, stop_ws),
861 (cls.KEEP_SERVER, run_keep, stop_keep),
862 (cls.KEEP_PROXY_SERVER, run_keep_proxy, stop_keep_proxy),
863 (cls.KEEP_WEB_SERVER, run_keep_web, stop_keep_web)):
864 if server_kwargs is not None:
865 start_func(**server_kwargs)
866 cls._cleanup_funcs.append(stop_func)
867 if (cls.KEEP_SERVER is None) and (cls.KEEP_PROXY_SERVER is None):
868 cls.local_store = tempfile.mkdtemp()
869 os.environ['KEEP_LOCAL_STORE'] = cls.local_store
870 cls._cleanup_funcs.append(
871 lambda: shutil.rmtree(cls.local_store, ignore_errors=True))
873 os.environ.pop('KEEP_LOCAL_STORE', None)
874 arvados.config.initialize()
877 def tearDownClass(cls):
878 for clean_func in cls._cleanup_funcs:
880 cls._restore_dict(cls._orig_environ, os.environ)
881 cls._restore_dict(cls._orig_config, arvados.config.settings())
884 if __name__ == "__main__":
887 'start_ws', 'stop_ws',
888 'start_controller', 'stop_controller',
889 'start_keep', 'stop_keep',
890 'start_keep_proxy', 'stop_keep_proxy',
891 'start_keep-web', 'stop_keep-web',
892 'start_arv-git-httpd', 'stop_arv-git-httpd',
893 'start_nginx', 'stop_nginx', 'setup_config',
895 parser = argparse.ArgumentParser()
896 parser.add_argument('action', type=str, help="one of {}".format(actions))
897 parser.add_argument('--auth', type=str, metavar='FIXTURE_NAME', help='Print authorization info for given api_client_authorizations fixture')
898 parser.add_argument('--num-keep-servers', metavar='int', type=int, default=2, help="Number of keep servers desired")
899 parser.add_argument('--keep-blob-signing', action="store_true", help="Enable blob signing for keepstore servers")
901 args = parser.parse_args()
903 if args.action not in actions:
904 print("Unrecognized action '{}'. Actions are: {}.".
905 format(args.action, actions),
908 if args.action == 'start':
909 stop(force=('ARVADOS_TEST_API_HOST' not in os.environ))
910 run(leave_running_atexit=True)
911 host = os.environ['ARVADOS_API_HOST']
912 if args.auth is not None:
913 token = auth_token(args.auth)
914 print("export ARVADOS_API_TOKEN={}".format(pipes.quote(token)))
915 print("export ARVADOS_API_HOST={}".format(pipes.quote(host)))
916 print("export ARVADOS_API_HOST_INSECURE=true")
919 elif args.action == 'stop':
920 stop(force=('ARVADOS_TEST_API_HOST' not in os.environ))
921 elif args.action == 'start_ws':
923 elif args.action == 'stop_ws':
925 elif args.action == 'start_controller':
927 elif args.action == 'stop_controller':
929 elif args.action == 'start_keep':
930 run_keep(blob_signing=args.keep_blob_signing, num_servers=args.num_keep_servers)
931 elif args.action == 'stop_keep':
932 stop_keep(num_servers=args.num_keep_servers)
933 elif args.action == 'start_keep_proxy':
935 elif args.action == 'stop_keep_proxy':
937 elif args.action == 'start_arv-git-httpd':
939 elif args.action == 'stop_arv-git-httpd':
941 elif args.action == 'start_keep-web':
943 elif args.action == 'stop_keep-web':
945 elif args.action == 'start_nginx':
947 print("export ARVADOS_API_HOST=0.0.0.0:{}".format(external_port_from_config('Controller')))
948 elif args.action == 'stop_nginx':
950 elif args.action == 'setup_config':
953 raise Exception("action recognized but not implemented!?")