15 """Run interop (cross-language) tests in parallel."""
17 from __future__
import print_function
23 import multiprocessing
40 atexit.register(
lambda: subprocess.call([
'stty',
'echo']))
42 ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]),
'../..'))
45 _FALLBACK_SERVER_PORT = 443
46 _BALANCER_SERVER_PORT = 12000
47 _BACKEND_SERVER_PORT = 8080
51 _FAKE_SERVERS_SAFENAME =
'fake_servers'
54 _SERVICE_NAME =
'server.test.google.fr'
64 return [
'bins/opt/interop_client'] + args
81 'GRPC_DEFAULT_SSL_ROOTS_FILE_PATH':
82 '/var/local/git/grpc/src/core/tsi/test_creds/ca.pem',
100 pem_to_der_cmd = (
'openssl x509 -outform der '
101 '-in /external_mount/src/core/tsi/test_creds/ca.pem '
102 '-out /tmp/test_ca.der')
103 keystore_import_cmd = (
105 '-keystore /usr/lib/jvm/java-8-oracle/jre/lib/security/cacerts '
106 '-file /tmp/test_ca.der '
107 '-deststorepass changeit '
111 (
'{pem_to_der_cmd} && '
112 '{keystore_import_cmd} && '
113 './run-test-client.sh {java_client_args}').
format(
114 pem_to_der_cmd=pem_to_der_cmd,
115 keystore_import_cmd=keystore_import_cmd,
116 java_client_args=
' '.join(args))
124 '-Dio.grpc.internal.DnsNameResolverProvider.enable_grpclb=true '
125 '-Djava.util.logging.config.file=/var/local/grpc_java_logging/logconf.txt'
136 self.
client_cwd =
'/go/src/google.golang.org/grpc/interop/client'
146 (
'cp /external_mount/src/core/tsi/test_creds/ca.pem '
147 '/etc/ssl/certs/ca-certificates.crt && '
148 '/go/bin/client {go_client_args}').
format(
149 go_client_args=
' '.join(args))
154 'GRPC_GO_LOG_VERBOSITY_LEVEL':
'3',
155 'GRPC_GO_LOG_SEVERITY_LEVEL':
'INFO'
170 """Wraps given cmdline array to create 'docker run' cmdline from it."""
172 docker_cmdline =
'docker run -i --rm=true'.
split()
174 for k, v
in list(environ.items()):
175 docker_cmdline += [
'-e',
'%s=%s' % (k, v)]
176 return docker_cmdline + [
'-w', cwd] + docker_args + [image] + cmdline
180 assert job._spec.container_name
181 dockerjob.docker_kill(job._spec.container_name)
186 if transport_security ==
'tls':
187 args += [
'--use_tls=true']
188 elif transport_security ==
'alts':
189 args += [
'--use_tls=false',
'--use_alts=true']
190 elif transport_security ==
'insecure':
191 args += [
'--use_tls=false']
192 elif transport_security ==
'google_default_credentials':
193 args += [
'--custom_credentials_type=google_default_credentials']
195 print(
'Invalid transport security option.')
203 transport_security='tls'):
204 """Runs a gRPC client under test in a docker container"""
205 interop_only_options = [
206 '--server_host=%s' % _SERVICE_NAME,
207 '--server_port=%d' % _FALLBACK_SERVER_PORT
212 if language.safename ==
'c++':
213 interop_only_options += [
'--server_host_override=""']
216 interop_only_options += [
'--use_test_ca=false']
217 client_args = language.client_cmd(interop_only_options)
218 container_name = dockerjob.random_name(
'lb_interop_client_%s' %
222 environ=language.global_env(),
224 cwd=language.client_cwd,
226 '--dns=%s' % dns_server_ip,
228 '--name=%s' % container_name,
230 '{grpc_grpc_root_dir}:/external_mount:ro'.
format(
231 grpc_grpc_root_dir=ROOT),
233 jobset.message(
'IDLE',
234 'docker_cmdline:\b|%s|' %
' '.join(docker_cmdline),
236 test_job = jobset.JobSpec(cmdline=docker_cmdline,
237 shortname=(
'lb_interop_client:%s' % language),
238 timeout_seconds=_TEST_TIMEOUT,
239 kill_handler=_job_kill_handler)
240 test_job.container_name = container_name
245 """Create jobspec for running a fallback server"""
248 '--port=%d' % _FALLBACK_SERVER_PORT,
255 """Create jobspec for running a backend server"""
258 '--port=%d' % _BACKEND_SERVER_PORT,
265 """Create jobspec for running a balancer server"""
268 '--backend_addrs=%s' %
','.join(backend_addrs),
269 '--port=%d' % _BALANCER_SERVER_PORT,
270 '--short_stream=%s' % short_stream,
271 '--service_name=%s' % _SERVICE_NAME,
278 container_name = dockerjob.random_name(shortname)
280 'GRPC_GO_LOG_VERBOSITY_LEVEL':
'3',
281 'GRPC_GO_LOG_SEVERITY_LEVEL':
'INFO ',
286 image=docker_images.get(_FAKE_SERVERS_SAFENAME),
288 docker_args=[
'--name=%s' % container_name])
289 jobset.message(
'IDLE',
290 'docker_cmdline:\b|%s|' %
' '.join(docker_cmdline),
292 server_job = jobset.JobSpec(cmdline=docker_cmdline,
294 timeout_seconds=30 * 60)
295 server_job.container_name = container_name
300 cause_no_error_no_data_for_balancer_a_record):
301 container_name = dockerjob.random_name(shortname)
302 run_dns_server_cmdline = [
304 'test/cpp/naming/utils/run_dns_server_for_lb_interop_tests.py',
305 '--grpclb_ips=%s' %
','.join(grpclb_ips),
306 '--fallback_ips=%s' %
','.join(fallback_ips),
308 if cause_no_error_no_data_for_balancer_a_record:
309 run_dns_server_cmdline.append(
310 '--cause_no_error_no_data_for_balancer_a_record')
312 run_dns_server_cmdline,
313 cwd=
'/var/local/git/grpc',
314 image=docker_images.get(_FAKE_SERVERS_SAFENAME),
315 docker_args=[
'--name=%s' % container_name])
316 jobset.message(
'IDLE',
317 'docker_cmdline:\b|%s|' %
' '.join(docker_cmdline),
319 server_job = jobset.JobSpec(cmdline=docker_cmdline,
321 timeout_seconds=30 * 60)
322 server_job.container_name = container_name
327 """Creates jobspec for building interop docker image for a language"""
328 tag =
'%s_%s:%s' % (basename_prefix, lang_safename, uuid.uuid4())
330 'INTEROP_IMAGE': tag,
331 'BASE_NAME':
'%s_%s' % (basename_prefix, lang_safename),
333 build_job = jobset.JobSpec(
334 cmdline=[
'tools/run_tests/dockerize/build_interop_image.sh'],
336 shortname=
'build_docker_%s' % lang_safename,
337 timeout_seconds=30 * 60)
342 argp = argparse.ArgumentParser(description=
'Run interop tests.')
343 argp.add_argument(
'-l',
345 choices=[
'all'] + sorted(_LANGUAGES),
348 help=
'Clients to run.')
349 argp.add_argument(
'-j',
'--jobs', default=multiprocessing.cpu_count(), type=int)
350 argp.add_argument(
'-s',
354 help=
'File containing test scenarios as JSON configs.')
361 'Useful for manual runs: specify the name of '
362 'the scenario to run from scenarios_file. Run all scenarios if unset.'))
363 argp.add_argument(
'--cxx_image_tag',
366 help=(
'Setting this skips the clients docker image '
367 'build step and runs the client from the named '
368 'image. Only supports running a one client language.'))
369 argp.add_argument(
'--go_image_tag',
372 help=(
'Setting this skips the clients docker image build '
373 'step and runs the client from the named image. Only '
374 'supports running a one client language.'))
375 argp.add_argument(
'--java_image_tag',
378 help=(
'Setting this skips the clients docker image build '
379 'step and runs the client from the named image. Only '
380 'supports running a one client language.'))
382 '--servers_image_tag',
385 help=(
'Setting this skips the fake servers docker image '
386 'build step and runs the servers from the named image.'))
387 argp.add_argument(
'--no_skips',
392 help=(
'Useful for manual runs. Setting this overrides test '
393 '"skips" configured in test scenarios.'))
394 argp.add_argument(
'--verbose',
399 help=
'Increase logging.')
400 args = argp.parse_args()
405 if len(args.language)
and args.language[0] ==
'all':
406 languages = list(_LANGUAGES.keys())
408 languages = args.language
409 for lang_name
in languages:
410 l = _LANGUAGES[lang_name]
413 if lang_name ==
'c++' and args.cxx_image_tag:
414 docker_images[
str(l.safename)] = args.cxx_image_tag
415 elif lang_name ==
'go' and args.go_image_tag:
416 docker_images[
str(l.safename)] = args.go_image_tag
417 elif lang_name ==
'java' and args.java_image_tag:
418 docker_images[
str(l.safename)] = args.java_image_tag
423 build_jobs.append(job)
424 docker_images[
str(l.safename)] = job.tag
427 if args.servers_image_tag:
428 docker_images[_FAKE_SERVERS_SAFENAME] = args.servers_image_tag
433 basename_prefix=
'lb_interop')
434 build_jobs.append(job)
435 docker_images[_FAKE_SERVERS_SAFENAME] = job.tag
438 jobset.message(
'START',
'Building interop docker images.', do_newline=
True)
439 print(
'Jobs to run: \n%s\n' %
'\n'.join(
str(j)
for j
in build_jobs))
440 num_failures, _ = jobset.run(build_jobs,
441 newline_on_success=
True,
443 if num_failures == 0:
444 jobset.message(
'SUCCESS',
445 'All docker images built successfully.',
448 jobset.message(
'FAILED',
449 'Failed to build interop docker images.',
455 """Probes the DNS server until it's running and safe for tests."""
456 for i
in range(0, 30):
457 print(
'Health check: attempt to connect to DNS server over TCP.')
458 tcp_connect_subprocess = subprocess.Popen([
459 os.path.join(os.getcwd(),
'test/cpp/naming/utils/tcp_connect.py'),
460 '--server_host', dns_server_ip,
'--server_port',
461 str(53),
'--timeout',
464 tcp_connect_subprocess.communicate()
465 if tcp_connect_subprocess.returncode == 0:
466 print((
'Health check: attempt to make an A-record '
467 'query to DNS server.'))
468 dns_resolver_subprocess = subprocess.Popen([
469 os.path.join(os.getcwd(),
470 'test/cpp/naming/utils/dns_resolver.py'),
472 (
'health-check-local-dns-server-is-alive.'
473 'resolver-tests.grpctestingexp'),
'--server_host',
474 dns_server_ip,
'--server_port',
477 stdout=subprocess.PIPE)
478 dns_resolver_stdout, _ = dns_resolver_subprocess.communicate()
479 if dns_resolver_subprocess.returncode == 0:
480 if '123.123.123.123' in dns_resolver_stdout:
481 print((
'DNS server is up! '
482 'Successfully reached it over UDP and TCP.'))
485 raise Exception((
'Failed to reach DNS server over TCP and/or UDP. '
486 'Exitting without running tests.'))
490 return '%s_%s_%d' % (shortname_prefix, shortname, index)
494 jobset.message(
'START',
'Run scenario: %s' % scenario_config[
'name'])
496 server_addresses = {}
497 suppress_server_logs =
True
502 shortname_prefix = scenario_config[
'name']
504 for i
in range(
len(scenario_config[
'backend_configs'])):
505 backend_config = scenario_config[
'backend_configs'][i]
506 backend_shortname =
shortname(shortname_prefix,
'backend_server', i)
508 backend_config[
'transport_sec'], backend_shortname)
509 backend_job = dockerjob.DockerJob(backend_spec)
510 server_jobs[backend_shortname] = backend_job
511 backend_addrs.append(
512 '%s:%d' % (backend_job.ip_address(), _BACKEND_SERVER_PORT))
514 for i
in range(
len(scenario_config[
'fallback_configs'])):
515 fallback_config = scenario_config[
'fallback_configs'][i]
516 fallback_shortname =
shortname(shortname_prefix,
'fallback_server',
519 fallback_config[
'transport_sec'], fallback_shortname)
520 fallback_job = dockerjob.DockerJob(fallback_spec)
521 server_jobs[fallback_shortname] = fallback_job
522 fallback_ips.append(fallback_job.ip_address())
524 for i
in range(
len(scenario_config[
'balancer_configs'])):
525 balancer_config = scenario_config[
'balancer_configs'][i]
526 grpclb_shortname =
shortname(shortname_prefix,
'grpclb_server', i)
528 balancer_config[
'short_stream'],
529 backend_addrs, grpclb_shortname)
530 grpclb_job = dockerjob.DockerJob(grpclb_spec)
531 server_jobs[grpclb_shortname] = grpclb_job
532 grpclb_ips.append(grpclb_job.ip_address())
534 dns_server_shortname =
shortname(shortname_prefix,
'dns_server', 0)
536 grpclb_ips, fallback_ips, dns_server_shortname,
537 scenario_config[
'cause_no_error_no_data_for_balancer_a_record'])
538 dns_server_job = dockerjob.DockerJob(dns_server_spec)
539 server_jobs[dns_server_shortname] = dns_server_job
545 dns_server_ip = dns_server_job.ip_address()
549 for lang_name
in languages:
552 if not args.no_skips
and lang_name
in scenario_config.get(
555 'IDLE',
'Skipping scenario: %s for language: %s\n' %
556 (scenario_config[
'name'], lang_name))
558 lang = _LANGUAGES[lang_name]
562 docker_image=docker_images.get(lang.safename),
563 transport_security=scenario_config[
'transport_sec'])
564 jobs.append(test_job)
566 'IDLE',
'Jobs to run: \n%s\n' %
'\n'.join(
str(job)
for job
in jobs))
567 num_failures, resultset = jobset.run(jobs,
568 newline_on_success=
True,
570 report_utils.render_junit_xml_report(resultset,
'sponge_log.xml')
572 suppress_server_logs =
False
573 jobset.message(
'FAILED',
574 'Scenario: %s. Some tests failed' %
575 scenario_config[
'name'],
578 jobset.message(
'SUCCESS',
579 'Scenario: %s. All tests passed' %
580 scenario_config[
'name'],
585 for server, job
in list(server_jobs.items()):
586 if not job.is_running():
587 print(
'Server "%s" has exited prematurely.' % server)
588 suppress_failure = suppress_server_logs
and not args.verbose
589 dockerjob.finish_jobs([j
for j
in six.itervalues(server_jobs)],
590 suppress_failure=suppress_failure)
594 with open(args.scenarios_file,
'r')
as scenarios_input:
595 all_scenarios = json.loads(scenarios_input.read())
596 for scenario
in all_scenarios:
597 if args.scenario_name:
598 if args.scenario_name != scenario[
'name']:
599 jobset.message(
'IDLE',
600 'Skipping scenario: %s' % scenario[
'name'])
603 if num_failures == 0: