blob: e20d11c0cfda0cbd344c81cc0e3a8939443b8ea8 [file] [log] [blame]
Jan Tattermusch320bd612015-09-15 12:44:35 -07001#!/usr/bin/env python
2# Copyright 2015, Google Inc.
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15# * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31"""Run interop (cross-language) tests in parallel."""
32
33import argparse
Jan Tattermusch91ad0182015-10-01 09:22:03 -070034import dockerjob
Jan Tattermusch320bd612015-09-15 12:44:35 -070035import itertools
36import xml.etree.cElementTree as ET
37import jobset
Jan Tattermusch210a0ea2015-10-02 15:05:36 -070038import multiprocessing
Jan Tattermusch8266c672015-09-17 09:18:03 -070039import os
40import subprocess
41import sys
Jan Tattermusch91ad0182015-10-01 09:22:03 -070042import tempfile
Jan Tattermusch8266c672015-09-17 09:18:03 -070043import time
Jan Tattermusch91ad0182015-10-01 09:22:03 -070044import uuid
Jan Tattermusch320bd612015-09-15 12:44:35 -070045
Jan Tattermusch91ad0182015-10-01 09:22:03 -070046ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))
47os.chdir(ROOT)
48
49_DEFAULT_SERVER_PORT=8080
Jan Tattermuschf49936a2015-09-16 15:44:26 -070050
51_CLOUD_TO_PROD_BASE_ARGS = [
52 '--server_host_override=grpc-test.sandbox.google.com',
53 '--server_host=grpc-test.sandbox.google.com',
54 '--server_port=443']
55
Jan Tattermusch8266c672015-09-17 09:18:03 -070056_CLOUD_TO_CLOUD_BASE_ARGS = [
57 '--server_host_override=foo.test.google.fr']
58
Jan Tattermuschf49936a2015-09-16 15:44:26 -070059# TOOD(jtattermusch) wrapped languages use this variable for location
60# of roots.pem. We might want to use GRPC_DEFAULT_SSL_ROOTS_FILE_PATH
61# supported by C core SslCredentials instead.
62_SSL_CERT_ENV = { 'SSL_CERT_FILE':'/usr/local/share/grpc/roots.pem' }
63
Jan Tattermuschcc1bde72015-10-05 12:47:45 -070064# TODO(jtattermusch) unify usage of --use_tls and --use_tls=true
65# TODO(jtattermusch) unify usage of --use_prod_roots and --use_test_ca
66# TODO(jtattermusch) go uses --tls_ca_file instead of --use_test_ca
Jan Tattermuschf49936a2015-09-16 15:44:26 -070067
Jan Tattermusch8266c672015-09-17 09:18:03 -070068
Jan Tattermuschf49936a2015-09-16 15:44:26 -070069class CXXLanguage:
70
71 def __init__(self):
72 self.client_cmdline_base = ['bins/opt/interop_client']
73 self.client_cwd = None
Jan Tattermusch91ad0182015-10-01 09:22:03 -070074 self.server_cwd = None
Jan Tattermuschf49936a2015-09-16 15:44:26 -070075
76 def cloud_to_prod_args(self):
77 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
yang-g54db83d2015-10-01 11:42:05 -070078 ['--use_tls=true','--use_prod_roots'])
Jan Tattermuschf49936a2015-09-16 15:44:26 -070079
Jan Tattermusch8266c672015-09-17 09:18:03 -070080 def cloud_to_cloud_args(self):
81 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
yang-g54db83d2015-10-01 11:42:05 -070082 ['--use_tls=true'])
Jan Tattermusch8266c672015-09-17 09:18:03 -070083
Jan Tattermuschf49936a2015-09-16 15:44:26 -070084 def cloud_to_prod_env(self):
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -070085 return {}
Jan Tattermuschf49936a2015-09-16 15:44:26 -070086
Jan Tattermusch91ad0182015-10-01 09:22:03 -070087 def server_args(self):
Jan Tattermusch28bf5592015-10-02 13:50:24 -070088 return ['bins/opt/interop_server', '--use_tls=true']
Jan Tattermusch91ad0182015-10-01 09:22:03 -070089
Jan Tattermuschf49936a2015-09-16 15:44:26 -070090 def __str__(self):
91 return 'c++'
92
93
94class CSharpLanguage:
95
96 def __init__(self):
97 self.client_cmdline_base = ['mono', 'Grpc.IntegrationTesting.Client.exe']
98 self.client_cwd = 'src/csharp/Grpc.IntegrationTesting.Client/bin/Debug'
Jan Tattermusch91ad0182015-10-01 09:22:03 -070099 self.server_cwd = 'src/csharp/Grpc.IntegrationTesting.Server/bin/Debug'
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700100
101 def cloud_to_prod_args(self):
102 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
103 ['--use_tls'])
104
Jan Tattermusch8266c672015-09-17 09:18:03 -0700105 def cloud_to_cloud_args(self):
106 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
107 ['--use_tls', '--use_test_ca'])
108
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700109 def cloud_to_prod_env(self):
110 return _SSL_CERT_ENV
111
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700112 def server_args(self):
113 return ['mono', 'Grpc.IntegrationTesting.Server.exe', '--use_tls']
114
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700115 def __str__(self):
116 return 'csharp'
117
118
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700119class JavaLanguage:
120
121 def __init__(self):
122 self.client_cmdline_base = ['./run-test-client.sh']
123 self.client_cwd = '../grpc-java'
124 self.server_cwd = '../grpc-java'
125
126 def cloud_to_prod_args(self):
127 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
128 ['--use_tls=true'])
129
130 def cloud_to_cloud_args(self):
131 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
132 ['--use_tls=true', '--use_test_ca=true'])
133
134 def cloud_to_prod_env(self):
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700135 return {}
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700136
137 def server_args(self):
138 return ['./run-test-server.sh', '--use_tls=true']
139
140 def __str__(self):
141 return 'java'
142
143
Jan Tattermuschcc1bde72015-10-05 12:47:45 -0700144class GoLanguage:
145
146 def __init__(self):
147 self.client_cmdline_base = ['go', 'run', 'client.go']
148 # TODO: this relies on running inside docker
149 self.client_cwd = '/go/src/google.golang.org/grpc/interop/client'
150 self.server_cwd = '/go/src/google.golang.org/grpc/interop/server'
151
152 def cloud_to_prod_args(self):
153 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
154 ['--use_tls=true', '--tls_ca_file=""'])
155
156 def cloud_to_cloud_args(self):
157 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
158 ['--use_tls=true'])
159
160 def cloud_to_prod_env(self):
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700161 return {}
Jan Tattermuschcc1bde72015-10-05 12:47:45 -0700162
163 def server_args(self):
164 return ['go', 'run', 'server.go', '--use_tls=true']
165
166 def __str__(self):
167 return 'go'
168
169
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700170class NodeLanguage:
171
172 def __init__(self):
173 self.client_cmdline_base = ['node', 'src/node/interop/interop_client.js']
174 self.client_cwd = None
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700175 self.server_cwd = None
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700176
177 def cloud_to_prod_args(self):
178 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
179 ['--use_tls=true'])
180
Jan Tattermusch8266c672015-09-17 09:18:03 -0700181 def cloud_to_cloud_args(self):
182 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
183 ['--use_tls=true', '--use_test_ca=true'])
184
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700185 def cloud_to_prod_env(self):
186 return _SSL_CERT_ENV
187
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700188 def server_args(self):
189 return ['node', 'src/node/interop/interop_server.js', '--use_tls=true']
190
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700191 def __str__(self):
192 return 'node'
193
Jan Tattermuschf88f3e22015-09-16 17:50:45 -0700194
195class PHPLanguage:
196
197 def __init__(self):
198 self.client_cmdline_base = ['src/php/bin/interop_client.sh']
199 self.client_cwd = None
200
201 def cloud_to_prod_args(self):
202 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
203 ['--use_tls'])
204
Jan Tattermusch8266c672015-09-17 09:18:03 -0700205 def cloud_to_cloud_args(self):
206 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
207 ['--use_tls', '--use_test_ca'])
208
Jan Tattermuschf88f3e22015-09-16 17:50:45 -0700209 def cloud_to_prod_env(self):
210 return _SSL_CERT_ENV
211
212 def __str__(self):
213 return 'php'
214
215
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700216class RubyLanguage:
217
218 def __init__(self):
219 self.client_cmdline_base = ['ruby', 'src/ruby/bin/interop/interop_client.rb']
220 self.client_cwd = None
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700221 self.server_cwd = None
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700222
223 def cloud_to_prod_args(self):
224 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
225 ['--use_tls'])
226
Jan Tattermusch8266c672015-09-17 09:18:03 -0700227 def cloud_to_cloud_args(self):
228 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
229 ['--use_tls', '--use_test_ca'])
230
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700231 def cloud_to_prod_env(self):
232 return _SSL_CERT_ENV
233
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700234 def server_args(self):
235 return ['ruby', 'src/ruby/bin/interop/interop_server.rb', '--use_tls']
236
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700237 def __str__(self):
238 return 'ruby'
239
240
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700241# TODO(jtattermusch): python once we get it working
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700242_LANGUAGES = {
243 'c++' : CXXLanguage(),
244 'csharp' : CSharpLanguage(),
Jan Tattermuschcc1bde72015-10-05 12:47:45 -0700245 'go' : GoLanguage(),
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700246 'java' : JavaLanguage(),
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700247 'node' : NodeLanguage(),
Jan Tattermuschf88f3e22015-09-16 17:50:45 -0700248 'php' : PHPLanguage(),
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700249 'ruby' : RubyLanguage(),
250}
Jan Tattermusch320bd612015-09-15 12:44:35 -0700251
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700252# languages supported as cloud_to_cloud servers
Jan Tattermusch8266c672015-09-17 09:18:03 -0700253# TODO(jtattermusch): enable other languages as servers as well
Jan Tattermuschcc1bde72015-10-05 12:47:45 -0700254_SERVERS = ['c++', 'node', 'csharp', 'java', 'go']
Jan Tattermusch8266c672015-09-17 09:18:03 -0700255
Jan Tattermusch210a0ea2015-10-02 15:05:36 -0700256# TODO(jtattermusch): add empty_stream once PHP starts supporting it.
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700257# TODO(jtattermusch): add timeout_on_sleeping_server once java starts supporting it.
Jan Tattermusch320bd612015-09-15 12:44:35 -0700258# TODO(jtattermusch): add support for auth tests.
259_TEST_CASES = ['large_unary', 'empty_unary', 'ping_pong',
Jan Tattermusch210a0ea2015-10-02 15:05:36 -0700260 'client_streaming', 'server_streaming',
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700261 'cancel_after_begin', 'cancel_after_first_response']
Jan Tattermusch320bd612015-09-15 12:44:35 -0700262
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700263_AUTH_TEST_CASES = ['compute_engine_creds', 'jwt_token_creds',
264 'oauth2_auth_token', 'per_rpc_creds']
265
Jan Tattermusch8266c672015-09-17 09:18:03 -0700266
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700267def docker_run_cmdline(cmdline, image, docker_args=[], cwd=None, environ=None):
268 """Wraps given cmdline array to create 'docker run' cmdline from it."""
269 docker_cmdline = ['docker', 'run', '-i', '--rm=true']
270
271 # turn environ into -e docker args
272 if environ:
273 for k,v in environ.iteritems():
274 docker_cmdline += ['-e', '%s=%s' % (k,v)]
275
276 # set working directory
277 workdir = '/var/local/git/grpc'
278 if cwd:
279 workdir = os.path.join(workdir, cwd)
280 docker_cmdline += ['-w', workdir]
281
282 docker_cmdline += docker_args + [image] + cmdline
283 return docker_cmdline
284
285
286def bash_login_cmdline(cmdline):
287 """Creates bash -l -c cmdline from args list."""
288 # Use login shell:
289 # * rvm and nvm require it
290 # * makes error messages clearer if executables are missing
291 return ['bash', '-l', '-c', ' '.join(cmdline)]
292
293
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700294def add_auth_options(language, test_case, cmdline, env):
295 """Returns (cmdline, env) tuple with cloud_to_prod_auth test options."""
296
297 language = str(language)
298 cmdline = list(cmdline)
299 env = env.copy()
300
301 # TODO(jtattermusch): this file path only works inside docker
302 key_filepath = '/root/service_account/stubbyCloudTestingTest-ee3fce360ac5.json'
303 oauth_scope_arg = '--oauth_scope=https://www.googleapis.com/auth/xapi.zoo'
304 key_file_arg = '--service_account_key_file=%s' % key_filepath
305 default_account_arg = '--default_service_account=830293263384-compute@developer.gserviceaccount.com'
306
307 if test_case in ['jwt_token_creds', 'per_rpc_creds', 'oauth2_auth_token']:
308 if language in ['csharp', 'node', 'php', 'ruby']:
309 env['GOOGLE_APPLICATION_CREDENTIALS'] = key_filepath
310 else:
311 cmdline += [key_file_arg]
312
313 if test_case in ['per_rpc_creds', 'oauth2_auth_token']:
314 cmdline += [oauth_scope_arg]
315
316 if test_case == 'compute_engine_creds':
317 cmdline += [oauth_scope_arg, default_account_arg]
318
319 return (cmdline, env)
320
321
322def cloud_to_prod_jobspec(language, test_case, docker_image=None, auth=False):
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700323 """Creates jobspec for cloud-to-prod interop test"""
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700324 cmdline = language.cloud_to_prod_args() + ['--test_case=%s' % test_case]
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700325 cwd = language.client_cwd
326 environ = language.cloud_to_prod_env()
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700327 if auth:
328 cmdline, environ = add_auth_options(language, test_case, cmdline, environ)
329 cmdline = bash_login_cmdline(cmdline)
330
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700331 if docker_image:
332 cmdline = docker_run_cmdline(cmdline, image=docker_image, cwd=cwd, environ=environ)
333 cwd = None
334 environ = None
335
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700336 suite_name='cloud_to_prod_auth' if auth else 'cloud_to_prod'
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700337 test_job = jobset.JobSpec(
338 cmdline=cmdline,
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700339 cwd=cwd,
340 environ=environ,
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700341 shortname="%s:%s:%s" % (suite_name, language, test_case),
Jan Tattermusch210a0ea2015-10-02 15:05:36 -0700342 timeout_seconds=2*60,
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700343 flake_retries=5 if args.allow_flakes else 0,
344 timeout_retries=2 if args.allow_flakes else 0)
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700345 return test_job
346
Jan Tattermusch8266c672015-09-17 09:18:03 -0700347
348def cloud_to_cloud_jobspec(language, test_case, server_name, server_host,
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700349 server_port, docker_image=None):
Jan Tattermusch8266c672015-09-17 09:18:03 -0700350 """Creates jobspec for cloud-to-cloud interop test"""
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700351 cmdline = bash_login_cmdline(language.cloud_to_cloud_args() +
352 ['--test_case=%s' % test_case,
353 '--server_host=%s' % server_host,
354 '--server_port=%s' % server_port ])
355 cwd = language.client_cwd
356 if docker_image:
357 cmdline = docker_run_cmdline(cmdline,
358 image=docker_image,
359 cwd=cwd,
360 docker_args=['--net=host'])
361 cwd = None
Jan Tattermusch8266c672015-09-17 09:18:03 -0700362 test_job = jobset.JobSpec(
363 cmdline=cmdline,
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700364 cwd=cwd,
Jan Tattermusch8266c672015-09-17 09:18:03 -0700365 shortname="cloud_to_cloud:%s:%s_server:%s" % (language, server_name,
366 test_case),
Jan Tattermusch210a0ea2015-10-02 15:05:36 -0700367 timeout_seconds=2*60,
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700368 flake_retries=5 if args.allow_flakes else 0,
369 timeout_retries=2 if args.allow_flakes else 0)
Jan Tattermusch8266c672015-09-17 09:18:03 -0700370 return test_job
371
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700372
373def server_jobspec(language, docker_image):
374 """Create jobspec for running a server"""
375 cidfile = tempfile.mktemp()
376 cmdline = bash_login_cmdline(language.server_args() +
377 ['--port=%s' % _DEFAULT_SERVER_PORT])
378 docker_cmdline = docker_run_cmdline(cmdline,
379 image=docker_image,
380 cwd=language.server_cwd,
381 docker_args=['-p', str(_DEFAULT_SERVER_PORT),
382 '--cidfile', cidfile])
383 server_job = jobset.JobSpec(
384 cmdline=docker_cmdline,
385 shortname="interop_server:%s" % language,
386 timeout_seconds=30*60)
387 server_job.cidfile = cidfile
388 return server_job
389
390
391def build_interop_image_jobspec(language, tag=None):
392 """Creates jobspec for building interop docker image for a language"""
393 safelang = str(language).replace("+", "x")
394 if not tag:
395 tag = 'grpc_interop_%s:%s' % (safelang, uuid.uuid4())
396 env = {'INTEROP_IMAGE': tag, 'BASE_NAME': 'grpc_interop_%s' % safelang}
397 if not args.travis:
398 env['TTY_FLAG'] = '-t'
399 build_job = jobset.JobSpec(
400 cmdline=['tools/jenkins/build_interop_image.sh'],
401 environ=env,
402 shortname="build_docker_%s" % (language),
403 timeout_seconds=30*60)
404 build_job.tag = tag
405 return build_job
406
407
Jan Tattermusch320bd612015-09-15 12:44:35 -0700408argp = argparse.ArgumentParser(description='Run interop tests.')
409argp.add_argument('-l', '--language',
410 choices=['all'] + sorted(_LANGUAGES),
411 nargs='+',
Jan Tattermusch8266c672015-09-17 09:18:03 -0700412 default=['all'],
413 help='Clients to run.')
Jan Tattermusch210a0ea2015-10-02 15:05:36 -0700414argp.add_argument('-j', '--jobs', default=multiprocessing.cpu_count(), type=int)
Jan Tattermusch8266c672015-09-17 09:18:03 -0700415argp.add_argument('--cloud_to_prod',
416 default=False,
417 action='store_const',
418 const=True,
419 help='Run cloud_to_prod tests.')
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700420argp.add_argument('--cloud_to_prod_auth',
421 default=False,
422 action='store_const',
423 const=True,
424 help='Run cloud_to_prod_auth tests.')
Jan Tattermusch8266c672015-09-17 09:18:03 -0700425argp.add_argument('-s', '--server',
426 choices=['all'] + sorted(_SERVERS),
427 action='append',
428 help='Run cloud_to_cloud servers in a separate docker ' +
429 'image. Servers can only be started automatically if ' +
430 '--use_docker option is enabled.',
431 default=[])
432argp.add_argument('--override_server',
433 action='append',
434 type=lambda kv: kv.split("="),
435 help='Use servername=HOST:PORT to explicitly specify a server. E.g. csharp=localhost:50000',
436 default=[])
437argp.add_argument('-t', '--travis',
438 default=False,
439 action='store_const',
440 const=True)
441argp.add_argument('--use_docker',
442 default=False,
443 action='store_const',
444 const=True,
445 help='Run all the interop tests under docker. That provides ' +
446 'additional isolation and prevents the need to install ' +
447 'language specific prerequisites. Only available on Linux.')
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700448argp.add_argument('--allow_flakes',
449 default=False,
450 action='store_const',
451 const=True,
452 help="Allow flaky tests to show as passing (re-runs failed tests up to five times)")
Jan Tattermusch320bd612015-09-15 12:44:35 -0700453args = argp.parse_args()
454
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700455servers = set(s for s in itertools.chain.from_iterable(_SERVERS
Jan Tattermusch8266c672015-09-17 09:18:03 -0700456 if x == 'all' else [x]
457 for x in args.server))
458
459if args.use_docker:
460 if not args.travis:
461 print 'Seen --use_docker flag, will run interop tests under docker.'
462 print
463 print 'IMPORTANT: The changes you are testing need to be locally committed'
464 print 'because only the committed changes in the current branch will be'
465 print 'copied to the docker environment.'
466 time.sleep(5)
467
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700468if not args.use_docker and servers:
469 print "Running interop servers is only supported with --use_docker option enabled."
470 sys.exit(1)
Jan Tattermusch8266c672015-09-17 09:18:03 -0700471
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700472languages = set(_LANGUAGES[l]
473 for l in itertools.chain.from_iterable(
474 _LANGUAGES.iterkeys() if x == 'all' else [x]
475 for x in args.language))
Jan Tattermusch320bd612015-09-15 12:44:35 -0700476
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700477docker_images={}
478if args.use_docker:
479 # languages for which to build docker images
480 languages_to_build = set(_LANGUAGES[k] for k in set([str(l) for l in languages] +
481 [s for s in servers]))
Jan Tattermusch8266c672015-09-17 09:18:03 -0700482
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700483 build_jobs = []
484 for l in languages_to_build:
485 job = build_interop_image_jobspec(l)
486 docker_images[str(l)] = job.tag
487 build_jobs.append(job)
Jan Tattermusch8266c672015-09-17 09:18:03 -0700488
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700489 if build_jobs:
490 jobset.message('START', 'Building interop docker images.', do_newline=True)
491 if jobset.run(build_jobs, newline_on_success=True, maxjobs=args.jobs):
492 jobset.message('SUCCESS', 'All docker images built successfully.', do_newline=True)
493 else:
494 jobset.message('FAILED', 'Failed to build interop docker images.', do_newline=True)
495 for image in docker_images.itervalues():
496 dockerjob.remove_image(image, skip_nonexistent=True)
497 exit(1);
Jan Tattermusch8266c672015-09-17 09:18:03 -0700498
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700499# Start interop servers.
500server_jobs={}
501server_addresses={}
502try:
503 for s in servers:
504 lang = str(s)
505 spec = server_jobspec(_LANGUAGES[lang], docker_images.get(lang))
506 job = dockerjob.DockerJob(spec)
507 server_jobs[lang] = job
508 server_addresses[lang] = ('localhost', job.mapped_port(_DEFAULT_SERVER_PORT))
Jan Tattermusch8266c672015-09-17 09:18:03 -0700509
Jan Tattermusch210a0ea2015-10-02 15:05:36 -0700510
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700511 jobs = []
512 if args.cloud_to_prod:
513 for language in languages:
514 for test_case in _TEST_CASES:
515 test_job = cloud_to_prod_jobspec(language, test_case,
516 docker_image=docker_images.get(str(language)))
517 jobs.append(test_job)
Jan Tattermusch320bd612015-09-15 12:44:35 -0700518
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700519 if args.cloud_to_prod_auth:
520 for language in languages:
521 for test_case in _AUTH_TEST_CASES:
522 test_job = cloud_to_prod_jobspec(language, test_case,
523 docker_image=docker_images.get(str(language)),
524 auth=True)
525 jobs.append(test_job)
526
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700527 for server in args.override_server:
528 server_name = server[0]
529 (server_host, server_port) = server[1].split(':')
530 server_addresses[server_name] = (server_host, server_port)
Jan Tattermusch320bd612015-09-15 12:44:35 -0700531
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700532 for server_name, server_address in server_addresses.iteritems():
533 (server_host, server_port) = server_address
534 for language in languages:
535 for test_case in _TEST_CASES:
536 test_job = cloud_to_cloud_jobspec(language,
537 test_case,
538 server_name,
539 server_host,
540 server_port,
541 docker_image=docker_images.get(str(language)))
542 jobs.append(test_job)
Jan Tattermusch320bd612015-09-15 12:44:35 -0700543
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700544 if not jobs:
545 print "No jobs to run."
546 for image in docker_images.itervalues():
547 dockerjob.remove_image(image, skip_nonexistent=True)
548 sys.exit(1)
549
550 root = ET.Element('testsuites')
551 testsuite = ET.SubElement(root, 'testsuite', id='1', package='grpc', name='tests')
552
553 if jobset.run(jobs, newline_on_success=True, maxjobs=args.jobs, xml_report=testsuite):
554 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
555 else:
556 jobset.message('FAILED', 'Some tests failed', do_newline=True)
557
558 tree = ET.ElementTree(root)
559 tree.write('report.xml', encoding='UTF-8')
560
561finally:
562 # Check if servers are still running.
563 for server, job in server_jobs.iteritems():
564 if not job.is_running():
565 print 'Server "%s" has exited prematurely.' % server
566
567 dockerjob.finish_jobs([j for j in server_jobs.itervalues()])
568
569 for image in docker_images.itervalues():
570 print 'Removing docker image %s' % image
571 dockerjob.remove_image(image)