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['ARVADOS_RAILS_LOG_TO_STDOUT'] = '1'
325 env.pop('ARVADOS_WEBSOCKETS', None)
326 env.pop('ARVADOS_TEST_API_HOST', None)
327 env.pop('ARVADOS_API_HOST', None)
328 env.pop('ARVADOS_API_HOST_INSECURE', None)
329 env.pop('ARVADOS_API_TOKEN', None)
330 if not os.environ.get('ARVADOS_DEBUG', ''):
331 logf = open(_logfilename('railsapi'), 'a')
333 logf = open(_logfilename('railsapi'), 'w')
334 railsapi = subprocess.Popen(
336 'passenger', 'start', '-p{}'.format(port),
337 '--pid-file', pid_file,
338 '--log-file', '/dev/stdout',
340 '--ssl-certificate', 'tmp/self-signed.pem',
341 '--ssl-certificate-key', 'tmp/self-signed.key'],
342 env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf)
344 if not leave_running_atexit:
345 atexit.register(kill_server_pid, pid_file, passenger_root=api_src_dir)
347 my_api_host = "127.0.0.1:"+str(port)
348 os.environ['ARVADOS_API_HOST'] = my_api_host
350 # Make sure the server has written its pid file and started
351 # listening on its TCP port
352 _wait_until_port_listens(port)
353 find_server_pid(pid_file)
356 os.chdir(restore_cwd)
359 """Reset the test server to fixture state.
361 This resets the ARVADOS_TEST_API_HOST provided by a parent process
362 if any, otherwise the server started by run().
364 It also resets ARVADOS_* environment vars to point to the test
365 server with admin credentials.
367 existing_api_host = os.environ.get('ARVADOS_TEST_API_HOST', my_api_host)
368 token = auth_token('admin')
369 httpclient = httplib2.Http(ca_certs=os.path.join(
370 SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.pem'))
372 'https://{}/database/reset'.format(existing_api_host),
374 headers={'Authorization': 'OAuth2 {}'.format(token), 'Connection':'close'})
376 os.environ['ARVADOS_API_HOST_INSECURE'] = 'true'
377 os.environ['ARVADOS_API_TOKEN'] = token
378 os.environ['ARVADOS_API_HOST'] = existing_api_host
380 def stop(force=False):
381 """Stop the API server, if one is running.
383 If force==False, kill it only if we started it ourselves. (This
384 supports the use case where a Python test suite calls run(), but
385 run() just uses the ARVADOS_TEST_API_HOST provided by the parent
386 process, and the test suite cleans up after itself by calling
387 stop(). In this case the test server provided by the parent
388 process should be left alone.)
390 If force==True, kill it even if we didn't start it
391 ourselves. (This supports the use case in __main__, where "run"
392 and "stop" happen in different processes.)
395 if force or my_api_host is not None:
396 kill_server_pid(_pidfile('api'))
400 with open(os.environ["ARVADOS_CONFIG"]) as f:
401 return yaml.safe_load(f)
403 def internal_port_from_config(service, idx=0):
405 sorted(list(get_config()["Clusters"]["zzzzz"]["Services"][service]["InternalURLs"].keys()))[idx]).
406 netloc.split(":")[1])
408 def external_port_from_config(service):
409 return int(urlparse(get_config()["Clusters"]["zzzzz"]["Services"][service]["ExternalURL"]).netloc.split(":")[1])
411 def run_controller():
412 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
415 logf = open(_logfilename('controller'), 'a')
416 port = internal_port_from_config("Controller")
417 controller = subprocess.Popen(
418 ["arvados-server", "controller"],
419 stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
420 with open(_pidfile('controller'), 'w') as f:
421 f.write(str(controller.pid))
422 _wait_until_port_listens(port)
425 def stop_controller():
426 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
428 kill_server_pid(_pidfile('controller'))
431 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
434 port = internal_port_from_config("Websocket")
435 logf = open(_logfilename('ws'), 'a')
436 ws = subprocess.Popen(
437 ["arvados-server", "ws"],
438 stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
439 with open(_pidfile('ws'), 'w') as f:
441 _wait_until_port_listens(port)
445 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
447 kill_server_pid(_pidfile('ws'))
449 def _start_keep(n, blob_signing=False):
450 datadir = os.path.join(TEST_TMPDIR, "keep%d.data"%n)
451 if os.path.exists(datadir):
452 shutil.rmtree(datadir)
454 port = internal_port_from_config("Keepstore", idx=n)
456 # Currently, if there are multiple InternalURLs for a single host,
457 # the only way to tell a keepstore process which one it's supposed
458 # to listen on is to supply a redacted version of the config, with
459 # the other InternalURLs removed.
460 conf = os.path.join(TEST_TMPDIR, "keep%d.yaml"%n)
461 confdata = get_config()
462 confdata['Clusters']['zzzzz']['Services']['Keepstore']['InternalURLs'] = {"http://127.0.0.1:%d"%port: {}}
463 confdata['Clusters']['zzzzz']['Collections']['BlobSigning'] = blob_signing
464 with open(conf, 'w') as f:
465 yaml.safe_dump(confdata, f)
466 keep_cmd = ["keepstore", "-config", conf]
468 with open(_logfilename('keep{}'.format(n)), 'a') as logf:
469 with open('/dev/null') as _stdin:
470 child = subprocess.Popen(
471 keep_cmd, stdin=_stdin, stdout=logf, stderr=logf, close_fds=True)
473 print('child.pid is %d'%child.pid, file=sys.stderr)
474 with open(_pidfile('keep{}'.format(n)), 'w') as f:
475 f.write(str(child.pid))
477 _wait_until_port_listens(port)
481 def run_keep(num_servers=2, **kwargs):
482 stop_keep(num_servers)
486 host=os.environ['ARVADOS_API_HOST'],
487 token=os.environ['ARVADOS_API_TOKEN'],
490 for d in api.keep_services().list(filters=[['service_type','=','disk']]).execute()['items']:
491 api.keep_services().delete(uuid=d['uuid']).execute()
492 for d in api.keep_disks().list().execute()['items']:
493 api.keep_disks().delete(uuid=d['uuid']).execute()
495 for d in range(0, num_servers):
496 port = _start_keep(d, **kwargs)
497 svc = api.keep_services().create(body={'keep_service': {
498 'uuid': 'zzzzz-bi6l4-keepdisk{:07d}'.format(d),
499 'service_host': 'localhost',
500 'service_port': port,
501 'service_type': 'disk',
502 'service_ssl_flag': False,
504 api.keep_disks().create(body={
505 'keep_disk': {'keep_service_uuid': svc['uuid'] }
508 # If keepproxy and/or keep-web is running, send SIGHUP to make
509 # them discover the new keepstore services.
510 for svc in ('keepproxy', 'keep-web'):
511 pidfile = _pidfile('keepproxy')
512 if os.path.exists(pidfile):
514 with open(pidfile) as pid:
515 os.kill(int(pid.read()), signal.SIGHUP)
520 kill_server_pid(_pidfile('keep{}'.format(n)))
522 def stop_keep(num_servers=2):
523 for n in range(0, num_servers):
526 def run_keep_proxy():
527 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
528 os.environ["ARVADOS_KEEP_SERVICES"] = "http://localhost:{}".format(internal_port_from_config('Keepproxy'))
532 port = internal_port_from_config("Keepproxy")
533 env = os.environ.copy()
534 env['ARVADOS_API_TOKEN'] = auth_token('anonymous')
535 logf = open(_logfilename('keepproxy'), 'a')
536 kp = subprocess.Popen(
537 ['keepproxy'], env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf, close_fds=True)
539 with open(_pidfile('keepproxy'), 'w') as f:
541 _wait_until_port_listens(port)
543 print("Using API %s token %s" % (os.environ['ARVADOS_API_HOST'], auth_token('admin')), file=sys.stdout)
546 host=os.environ['ARVADOS_API_HOST'],
547 token=auth_token('admin'),
549 for d in api.keep_services().list(
550 filters=[['service_type','=','proxy']]).execute()['items']:
551 api.keep_services().delete(uuid=d['uuid']).execute()
552 api.keep_services().create(body={'keep_service': {
553 'service_host': 'localhost',
554 'service_port': port,
555 'service_type': 'proxy',
556 'service_ssl_flag': False,
558 os.environ["ARVADOS_KEEP_SERVICES"] = "http://localhost:{}".format(port)
559 _wait_until_port_listens(port)
561 def stop_keep_proxy():
562 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
564 kill_server_pid(_pidfile('keepproxy'))
566 def run_arv_git_httpd():
567 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
571 gitport = internal_port_from_config("GitHTTP")
572 env = os.environ.copy()
573 env.pop('ARVADOS_API_TOKEN', None)
574 logf = open(_logfilename('arv-git-httpd'), 'a')
575 agh = subprocess.Popen(['arv-git-httpd'],
576 env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf)
577 with open(_pidfile('arv-git-httpd'), 'w') as f:
578 f.write(str(agh.pid))
579 _wait_until_port_listens(gitport)
581 def stop_arv_git_httpd():
582 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
584 kill_server_pid(_pidfile('arv-git-httpd'))
587 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
591 keepwebport = internal_port_from_config("WebDAV")
592 env = os.environ.copy()
593 logf = open(_logfilename('keep-web'), 'a')
594 keepweb = subprocess.Popen(
596 env=env, stdin=open('/dev/null'), stdout=logf, stderr=logf)
597 with open(_pidfile('keep-web'), 'w') as f:
598 f.write(str(keepweb.pid))
599 _wait_until_port_listens(keepwebport)
602 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
604 kill_server_pid(_pidfile('keep-web'))
607 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
611 nginxconf['LISTENHOST'] = 'localhost'
612 nginxconf['CONTROLLERPORT'] = internal_port_from_config("Controller")
613 nginxconf['CONTROLLERSSLPORT'] = external_port_from_config("Controller")
614 nginxconf['KEEPWEBPORT'] = internal_port_from_config("WebDAV")
615 nginxconf['KEEPWEBDLSSLPORT'] = external_port_from_config("WebDAVDownload")
616 nginxconf['KEEPWEBSSLPORT'] = external_port_from_config("WebDAV")
617 nginxconf['KEEPPROXYPORT'] = internal_port_from_config("Keepproxy")
618 nginxconf['KEEPPROXYSSLPORT'] = external_port_from_config("Keepproxy")
619 nginxconf['GITPORT'] = internal_port_from_config("GitHTTP")
620 nginxconf['GITSSLPORT'] = external_port_from_config("GitHTTP")
621 nginxconf['HEALTHPORT'] = internal_port_from_config("Health")
622 nginxconf['HEALTHSSLPORT'] = external_port_from_config("Health")
623 nginxconf['WSPORT'] = internal_port_from_config("Websocket")
624 nginxconf['WSSSLPORT'] = external_port_from_config("Websocket")
625 nginxconf['WORKBENCH1PORT'] = internal_port_from_config("Workbench1")
626 nginxconf['WORKBENCH1SSLPORT'] = external_port_from_config("Workbench1")
627 nginxconf['SSLCERT'] = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.pem')
628 nginxconf['SSLKEY'] = os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'self-signed.key')
629 nginxconf['ACCESSLOG'] = _logfilename('nginx_access')
630 nginxconf['ERRORLOG'] = _logfilename('nginx_error')
631 nginxconf['TMPDIR'] = TEST_TMPDIR
633 conftemplatefile = os.path.join(MY_DIRNAME, 'nginx.conf')
634 conffile = os.path.join(TEST_TMPDIR, 'nginx.conf')
635 with open(conffile, 'w') as f:
637 r'{{([A-Z]+[A-Z0-9]+)}}',
638 lambda match: str(nginxconf.get(match.group(1))),
639 open(conftemplatefile).read()))
641 env = os.environ.copy()
642 env['PATH'] = env['PATH']+':/sbin:/usr/sbin:/usr/local/sbin'
644 nginx = subprocess.Popen(
646 '-g', 'error_log stderr info;',
647 '-g', 'pid '+_pidfile('nginx')+';',
649 env=env, stdin=open('/dev/null'), stdout=sys.stderr)
650 _wait_until_port_listens(nginxconf['CONTROLLERSSLPORT'])
653 rails_api_port = find_available_port()
654 controller_port = find_available_port()
655 controller_external_port = find_available_port()
656 websocket_port = find_available_port()
657 websocket_external_port = find_available_port()
658 workbench1_port = find_available_port()
659 workbench1_external_port = find_available_port()
660 git_httpd_port = find_available_port()
661 git_httpd_external_port = find_available_port()
662 health_httpd_port = find_available_port()
663 health_httpd_external_port = find_available_port()
664 keepproxy_port = find_available_port()
665 keepproxy_external_port = find_available_port()
666 keepstore_ports = sorted([str(find_available_port()) for _ in range(0,4)])
667 keep_web_port = find_available_port()
668 keep_web_external_port = find_available_port()
669 keep_web_dl_port = find_available_port()
670 keep_web_dl_external_port = find_available_port()
672 configsrc = os.environ.get("CONFIGSRC", None)
674 clusterconf = os.path.join(configsrc, "config.yml")
675 print("Getting config from %s" % clusterconf, file=sys.stderr)
676 pgconnection = yaml.safe_load(open(clusterconf))["Clusters"]["zzzzz"]["PostgreSQL"]["Connection"]
678 # assume "arvados-server install -type test" has set up the
679 # conventional db credentials
681 "client_encoding": "utf8",
683 "dbname": "arvados_test",
685 "password": "insecure_arvados_test",
688 localhost = "127.0.0.1"
692 "https://%s:%s"%(localhost, rails_api_port): {},
696 "ExternalURL": "https://%s:%s" % (localhost, controller_external_port),
698 "http://%s:%s"%(localhost, controller_port): {},
702 "ExternalURL": "wss://%s:%s/websocket" % (localhost, websocket_external_port),
704 "http://%s:%s"%(localhost, websocket_port): {},
708 "ExternalURL": "https://%s:%s/" % (localhost, workbench1_external_port),
710 "http://%s:%s"%(localhost, workbench1_port): {},
714 "ExternalURL": "https://%s:%s" % (localhost, git_httpd_external_port),
716 "http://%s:%s"%(localhost, git_httpd_port): {}
720 "ExternalURL": "https://%s:%s" % (localhost, health_httpd_external_port),
722 "http://%s:%s"%(localhost, health_httpd_port): {}
727 "http://%s:%s"%(localhost, port): {} for port in keepstore_ports
731 "ExternalURL": "https://%s:%s" % (localhost, keepproxy_external_port),
733 "http://%s:%s"%(localhost, keepproxy_port): {},
737 "ExternalURL": "https://%s:%s" % (localhost, keep_web_external_port),
739 "http://%s:%s"%(localhost, keep_web_port): {},
743 "ExternalURL": "https://%s:%s" % (localhost, keep_web_dl_external_port),
745 "http://%s:%s"%(localhost, keep_web_dl_port): {},
749 "ExternalURL": "http://localhost:3002",
756 "ManagementToken": "e687950a23c3a9bceec28c6223a06c79",
757 "SystemRootToken": auth_token('system_user'),
759 "RequestTimeout": "30s",
760 "RailsSessionSecretToken": "e24205c490ac07e028fd5f8a692dcb398bcd654eff1aef5f9fe6891994b18483",
764 "ProviderAppID": "arvados-server",
765 "ProviderAppSecret": "608dbf356a327e2d0d4932b60161e212c2d8d8f5e25690d7b622f850a990cd33",
769 "LogLevel": ('info' if os.environ.get('ARVADOS_DEBUG', '') in ['','0'] else 'debug'),
772 "Connection": pgconnection,
777 "Services": services,
779 "AnonymousUserToken": auth_token('anonymous'),
780 "UserProfileNotificationAddress": "arvados@example.com",
783 "BlobSigningKey": "zfhgfenhffzltr9dixws36j1yhksjoll2grmku38mi7yxd66h5j4q9w4jzanezacp8s6q0ro3hxakfye02152hncy6zml2ed0uc",
784 "TrustAllContent": False,
785 "ForwardSlashNameSubstitution": "/",
786 "TrashSweepInterval": "-1s",
789 "Repositories": os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'git', 'test'),
793 "GitInternalDir": os.path.join(SERVICES_SRC_DIR, 'api', 'tmp', 'internal.git'),
795 "SupportedDockerImageFormats": {"v1": {}},
798 "zzzzz-nyw5e-%015d"%n: {
800 "http://%s:%s" % (localhost, keepstore_ports[n]): {},
802 "Driver": "Directory",
803 "DriverParameters": {
804 "Root": os.path.join(TEST_TMPDIR, "keep%d.data"%n),
806 } for n in range(len(keepstore_ports))
812 conf = os.path.join(TEST_TMPDIR, 'arvados.yml')
813 with open(conf, 'w') as f:
814 yaml.safe_dump(config, f)
816 ex = "export ARVADOS_CONFIG="+conf
821 if 'ARVADOS_TEST_PROXY_SERVICES' in os.environ:
823 kill_server_pid(_pidfile('nginx'))
825 def _pidfile(program):
826 return os.path.join(TEST_TMPDIR, program + '.pid')
829 '''load a fixture yaml file'''
830 with open(os.path.join(SERVICES_SRC_DIR, 'api', "test", "fixtures",
834 trim_index = yaml_file.index("# Test Helper trims the rest of the file")
835 yaml_file = yaml_file[0:trim_index]
838 return yaml.safe_load(yaml_file)
840 def auth_token(token_name):
841 return fixture("api_client_authorizations")[token_name]["api_token"]
843 def authorize_with(token_name):
844 '''token_name is the symbolic name of the token from the api_client_authorizations fixture'''
845 arvados.config.settings()["ARVADOS_API_TOKEN"] = auth_token(token_name)
846 arvados.config.settings()["ARVADOS_API_HOST"] = os.environ.get("ARVADOS_API_HOST")
847 arvados.config.settings()["ARVADOS_API_HOST_INSECURE"] = "true"
849 class TestCaseWithServers(unittest.TestCase):
850 """TestCase to start and stop supporting Arvados servers.
852 Define any of MAIN_SERVER, KEEP_SERVER, and/or KEEP_PROXY_SERVER
853 class variables as a dictionary of keyword arguments. If you do,
854 setUpClass will start the corresponding servers by passing these
855 keyword arguments to the run, run_keep, and/or run_keep_server
856 functions, respectively. It will also set Arvados environment
857 variables to point to these servers appropriately. If you don't
858 run a Keep or Keep proxy server, setUpClass will set up a
859 temporary directory for Keep local storage, and set it as
862 tearDownClass will stop any servers started, and restore the
863 original environment.
868 KEEP_PROXY_SERVER = None
869 KEEP_WEB_SERVER = None
872 def _restore_dict(src, dest):
873 for key in list(dest.keys()):
880 cls._orig_environ = os.environ.copy()
881 cls._orig_config = arvados.config.settings().copy()
882 cls._cleanup_funcs = []
883 os.environ.pop('ARVADOS_KEEP_SERVICES', None)
884 os.environ.pop('ARVADOS_EXTERNAL_CLIENT', None)
885 for server_kwargs, start_func, stop_func in (
886 (cls.MAIN_SERVER, run, reset),
887 (cls.WS_SERVER, run_ws, stop_ws),
888 (cls.KEEP_SERVER, run_keep, stop_keep),
889 (cls.KEEP_PROXY_SERVER, run_keep_proxy, stop_keep_proxy),
890 (cls.KEEP_WEB_SERVER, run_keep_web, stop_keep_web)):
891 if server_kwargs is not None:
892 start_func(**server_kwargs)
893 cls._cleanup_funcs.append(stop_func)
894 if (cls.KEEP_SERVER is None) and (cls.KEEP_PROXY_SERVER is None):
895 cls.local_store = tempfile.mkdtemp()
896 os.environ['KEEP_LOCAL_STORE'] = cls.local_store
897 cls._cleanup_funcs.append(
898 lambda: shutil.rmtree(cls.local_store, ignore_errors=True))
900 os.environ.pop('KEEP_LOCAL_STORE', None)
901 arvados.config.initialize()
904 def tearDownClass(cls):
905 for clean_func in cls._cleanup_funcs:
907 cls._restore_dict(cls._orig_environ, os.environ)
908 cls._restore_dict(cls._orig_config, arvados.config.settings())
911 if __name__ == "__main__":
914 'start_ws', 'stop_ws',
915 'start_controller', 'stop_controller',
916 'start_keep', 'stop_keep',
917 'start_keep_proxy', 'stop_keep_proxy',
918 'start_keep-web', 'stop_keep-web',
919 'start_arv-git-httpd', 'stop_arv-git-httpd',
920 'start_nginx', 'stop_nginx', 'setup_config',
922 parser = argparse.ArgumentParser()
923 parser.add_argument('action', type=str, help="one of {}".format(actions))
924 parser.add_argument('--auth', type=str, metavar='FIXTURE_NAME', help='Print authorization info for given api_client_authorizations fixture')
925 parser.add_argument('--num-keep-servers', metavar='int', type=int, default=2, help="Number of keep servers desired")
926 parser.add_argument('--keep-blob-signing', action="store_true", help="Enable blob signing for keepstore servers")
928 args = parser.parse_args()
930 if args.action not in actions:
931 print("Unrecognized action '{}'. Actions are: {}.".
932 format(args.action, actions),
935 if args.action == 'start':
936 stop(force=('ARVADOS_TEST_API_HOST' not in os.environ))
937 run(leave_running_atexit=True)
938 host = os.environ['ARVADOS_API_HOST']
939 if args.auth is not None:
940 token = auth_token(args.auth)
941 print("export ARVADOS_API_TOKEN={}".format(pipes.quote(token)))
942 print("export ARVADOS_API_HOST={}".format(pipes.quote(host)))
943 print("export ARVADOS_API_HOST_INSECURE=true")
946 elif args.action == 'stop':
947 stop(force=('ARVADOS_TEST_API_HOST' not in os.environ))
948 elif args.action == 'start_ws':
950 elif args.action == 'stop_ws':
952 elif args.action == 'start_controller':
954 elif args.action == 'stop_controller':
956 elif args.action == 'start_keep':
957 run_keep(blob_signing=args.keep_blob_signing, num_servers=args.num_keep_servers)
958 elif args.action == 'stop_keep':
959 stop_keep(num_servers=args.num_keep_servers)
960 elif args.action == 'start_keep_proxy':
962 elif args.action == 'stop_keep_proxy':
964 elif args.action == 'start_arv-git-httpd':
966 elif args.action == 'stop_arv-git-httpd':
968 elif args.action == 'start_keep-web':
970 elif args.action == 'stop_keep-web':
972 elif args.action == 'start_nginx':
974 print("export ARVADOS_API_HOST=0.0.0.0:{}".format(external_port_from_config('Controller')))
975 elif args.action == 'stop_nginx':
977 elif args.action == 'setup_config':
980 raise Exception("action recognized but not implemented!?")