blob: 6daa967bba488f01f0e8a589a205aa72a3c079ce [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
Jan Tattermuschcc1bde72015-10-05 12:47:45 -070065# TODO(jtattermusch) go uses --tls_ca_file instead of --use_test_ca
Jan Tattermuschf49936a2015-09-16 15:44:26 -070066
Jan Tattermusch8266c672015-09-17 09:18:03 -070067
Jan Tattermuschf49936a2015-09-16 15:44:26 -070068class CXXLanguage:
69
70 def __init__(self):
71 self.client_cmdline_base = ['bins/opt/interop_client']
72 self.client_cwd = None
Jan Tattermusch91ad0182015-10-01 09:22:03 -070073 self.server_cwd = None
Jan Tattermusch0a14f622015-10-09 14:34:29 -070074 self.safename = 'cxx'
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-ga006b5f2015-10-07 13:14:35 -070078 ['--use_tls=true'])
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-ga006b5f2015-10-07 13:14:35 -070082 ['--use_tls=true', '--use_test_ca=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 Tattermusch0a14f622015-10-09 14:34:29 -0700100 self.safename = str(self)
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700101
102 def cloud_to_prod_args(self):
103 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
Jan Tattermusch7828e812015-10-07 17:27:48 -0700104 ['--use_tls=true'])
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700105
Jan Tattermusch8266c672015-09-17 09:18:03 -0700106 def cloud_to_cloud_args(self):
107 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
Jan Tattermusch7828e812015-10-07 17:27:48 -0700108 ['--use_tls=true', '--use_test_ca=true'])
Jan Tattermusch8266c672015-09-17 09:18:03 -0700109
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700110 def cloud_to_prod_env(self):
111 return _SSL_CERT_ENV
112
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700113 def server_args(self):
Jan Tattermusch7828e812015-10-07 17:27:48 -0700114 return ['mono', 'Grpc.IntegrationTesting.Server.exe', '--use_tls=true']
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700115
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700116 def __str__(self):
117 return 'csharp'
118
119
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700120class JavaLanguage:
121
122 def __init__(self):
123 self.client_cmdline_base = ['./run-test-client.sh']
124 self.client_cwd = '../grpc-java'
125 self.server_cwd = '../grpc-java'
Jan Tattermusch0a14f622015-10-09 14:34:29 -0700126 self.safename = str(self)
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700127
128 def cloud_to_prod_args(self):
129 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
130 ['--use_tls=true'])
131
132 def cloud_to_cloud_args(self):
133 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
134 ['--use_tls=true', '--use_test_ca=true'])
135
136 def cloud_to_prod_env(self):
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700137 return {}
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700138
139 def server_args(self):
140 return ['./run-test-server.sh', '--use_tls=true']
141
142 def __str__(self):
143 return 'java'
144
145
Jan Tattermuschcc1bde72015-10-05 12:47:45 -0700146class GoLanguage:
147
148 def __init__(self):
149 self.client_cmdline_base = ['go', 'run', 'client.go']
150 # TODO: this relies on running inside docker
151 self.client_cwd = '/go/src/google.golang.org/grpc/interop/client'
152 self.server_cwd = '/go/src/google.golang.org/grpc/interop/server'
Jan Tattermusch0a14f622015-10-09 14:34:29 -0700153 self.safename = str(self)
Jan Tattermuschcc1bde72015-10-05 12:47:45 -0700154
155 def cloud_to_prod_args(self):
156 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
157 ['--use_tls=true', '--tls_ca_file=""'])
158
159 def cloud_to_cloud_args(self):
160 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
161 ['--use_tls=true'])
162
163 def cloud_to_prod_env(self):
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700164 return {}
Jan Tattermuschcc1bde72015-10-05 12:47:45 -0700165
166 def server_args(self):
167 return ['go', 'run', 'server.go', '--use_tls=true']
168
169 def __str__(self):
170 return 'go'
171
172
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700173class NodeLanguage:
174
175 def __init__(self):
176 self.client_cmdline_base = ['node', 'src/node/interop/interop_client.js']
177 self.client_cwd = None
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700178 self.server_cwd = None
Jan Tattermusch0a14f622015-10-09 14:34:29 -0700179 self.safename = str(self)
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700180
181 def cloud_to_prod_args(self):
182 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
183 ['--use_tls=true'])
184
Jan Tattermusch8266c672015-09-17 09:18:03 -0700185 def cloud_to_cloud_args(self):
186 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
187 ['--use_tls=true', '--use_test_ca=true'])
188
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700189 def cloud_to_prod_env(self):
190 return _SSL_CERT_ENV
191
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700192 def server_args(self):
193 return ['node', 'src/node/interop/interop_server.js', '--use_tls=true']
194
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700195 def __str__(self):
196 return 'node'
197
Jan Tattermuschf88f3e22015-09-16 17:50:45 -0700198
199class PHPLanguage:
200
201 def __init__(self):
202 self.client_cmdline_base = ['src/php/bin/interop_client.sh']
203 self.client_cwd = None
Jan Tattermusch0a14f622015-10-09 14:34:29 -0700204 self.safename = str(self)
Jan Tattermuschf88f3e22015-09-16 17:50:45 -0700205
206 def cloud_to_prod_args(self):
207 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
208 ['--use_tls'])
209
Jan Tattermusch8266c672015-09-17 09:18:03 -0700210 def cloud_to_cloud_args(self):
211 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
212 ['--use_tls', '--use_test_ca'])
213
Jan Tattermuschf88f3e22015-09-16 17:50:45 -0700214 def cloud_to_prod_env(self):
215 return _SSL_CERT_ENV
216
217 def __str__(self):
218 return 'php'
219
220
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700221class RubyLanguage:
222
223 def __init__(self):
224 self.client_cmdline_base = ['ruby', 'src/ruby/bin/interop/interop_client.rb']
225 self.client_cwd = None
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700226 self.server_cwd = None
Jan Tattermusch0a14f622015-10-09 14:34:29 -0700227 self.safename = str(self)
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700228
229 def cloud_to_prod_args(self):
230 return (self.client_cmdline_base + _CLOUD_TO_PROD_BASE_ARGS +
231 ['--use_tls'])
232
Jan Tattermusch8266c672015-09-17 09:18:03 -0700233 def cloud_to_cloud_args(self):
234 return (self.client_cmdline_base + _CLOUD_TO_CLOUD_BASE_ARGS +
235 ['--use_tls', '--use_test_ca'])
236
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700237 def cloud_to_prod_env(self):
238 return _SSL_CERT_ENV
239
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700240 def server_args(self):
241 return ['ruby', 'src/ruby/bin/interop/interop_server.rb', '--use_tls']
242
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700243 def __str__(self):
244 return 'ruby'
245
246
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700247# TODO(jtattermusch): python once we get it working
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700248_LANGUAGES = {
249 'c++' : CXXLanguage(),
250 'csharp' : CSharpLanguage(),
Jan Tattermuschcc1bde72015-10-05 12:47:45 -0700251 'go' : GoLanguage(),
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700252 'java' : JavaLanguage(),
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700253 'node' : NodeLanguage(),
Jan Tattermuschf88f3e22015-09-16 17:50:45 -0700254 'php' : PHPLanguage(),
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700255 'ruby' : RubyLanguage(),
256}
Jan Tattermusch320bd612015-09-15 12:44:35 -0700257
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700258# languages supported as cloud_to_cloud servers
Jan Tattermuschb1dec722015-10-06 11:46:01 -0700259_SERVERS = ['c++', 'node', 'csharp', 'java', 'go', 'ruby']
Jan Tattermusch8266c672015-09-17 09:18:03 -0700260
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700261# TODO(jtattermusch): add timeout_on_sleeping_server once java starts supporting it.
Jan Tattermusch320bd612015-09-15 12:44:35 -0700262_TEST_CASES = ['large_unary', 'empty_unary', 'ping_pong',
Jan Tattermusch1f1c5c52015-10-09 14:36:28 -0700263 'empty_stream', 'client_streaming', 'server_streaming',
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700264 'cancel_after_begin', 'cancel_after_first_response']
Jan Tattermusch320bd612015-09-15 12:44:35 -0700265
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700266_AUTH_TEST_CASES = ['compute_engine_creds', 'jwt_token_creds',
267 'oauth2_auth_token', 'per_rpc_creds']
268
Jan Tattermusch8266c672015-09-17 09:18:03 -0700269
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700270def docker_run_cmdline(cmdline, image, docker_args=[], cwd=None, environ=None):
271 """Wraps given cmdline array to create 'docker run' cmdline from it."""
272 docker_cmdline = ['docker', 'run', '-i', '--rm=true']
273
274 # turn environ into -e docker args
275 if environ:
276 for k,v in environ.iteritems():
277 docker_cmdline += ['-e', '%s=%s' % (k,v)]
278
279 # set working directory
280 workdir = '/var/local/git/grpc'
281 if cwd:
282 workdir = os.path.join(workdir, cwd)
283 docker_cmdline += ['-w', workdir]
284
285 docker_cmdline += docker_args + [image] + cmdline
286 return docker_cmdline
287
288
289def bash_login_cmdline(cmdline):
290 """Creates bash -l -c cmdline from args list."""
291 # Use login shell:
292 # * rvm and nvm require it
293 # * makes error messages clearer if executables are missing
294 return ['bash', '-l', '-c', ' '.join(cmdline)]
295
296
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700297def add_auth_options(language, test_case, cmdline, env):
298 """Returns (cmdline, env) tuple with cloud_to_prod_auth test options."""
299
300 language = str(language)
301 cmdline = list(cmdline)
302 env = env.copy()
303
304 # TODO(jtattermusch): this file path only works inside docker
305 key_filepath = '/root/service_account/stubbyCloudTestingTest-ee3fce360ac5.json'
306 oauth_scope_arg = '--oauth_scope=https://www.googleapis.com/auth/xapi.zoo'
307 key_file_arg = '--service_account_key_file=%s' % key_filepath
308 default_account_arg = '--default_service_account=830293263384-compute@developer.gserviceaccount.com'
309
310 if test_case in ['jwt_token_creds', 'per_rpc_creds', 'oauth2_auth_token']:
311 if language in ['csharp', 'node', 'php', 'ruby']:
312 env['GOOGLE_APPLICATION_CREDENTIALS'] = key_filepath
313 else:
314 cmdline += [key_file_arg]
315
316 if test_case in ['per_rpc_creds', 'oauth2_auth_token']:
317 cmdline += [oauth_scope_arg]
318
Jan Tattermusch64d7c242015-10-08 08:02:27 -0700319 if test_case == 'oauth2_auth_token' and language == 'c++':
320 # C++ oauth2 test uses GCE creds and thus needs to know the default account
321 cmdline += [default_account_arg]
322
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700323 if test_case == 'compute_engine_creds':
324 cmdline += [oauth_scope_arg, default_account_arg]
325
326 return (cmdline, env)
327
328
Jan Tattermusche2686282015-10-08 16:27:07 -0700329def _job_kill_handler(job):
330 if job._spec.container_name:
331 dockerjob.docker_kill(job._spec.container_name)
332
333
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700334def cloud_to_prod_jobspec(language, test_case, docker_image=None, auth=False):
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700335 """Creates jobspec for cloud-to-prod interop test"""
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700336 cmdline = language.cloud_to_prod_args() + ['--test_case=%s' % test_case]
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700337 cwd = language.client_cwd
338 environ = language.cloud_to_prod_env()
Jan Tattermusche2686282015-10-08 16:27:07 -0700339 container_name = None
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700340 if auth:
341 cmdline, environ = add_auth_options(language, test_case, cmdline, environ)
342 cmdline = bash_login_cmdline(cmdline)
343
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700344 if docker_image:
Jan Tattermusch0a14f622015-10-09 14:34:29 -0700345 container_name = dockerjob.random_name('interop_client_%s' % language.safename)
Jan Tattermusche2686282015-10-08 16:27:07 -0700346 cmdline = docker_run_cmdline(cmdline,
347 image=docker_image,
348 cwd=cwd,
349 environ=environ,
350 docker_args=['--net=host',
351 '--name', container_name])
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700352 cwd = None
353 environ = None
354
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700355 suite_name='cloud_to_prod_auth' if auth else 'cloud_to_prod'
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700356 test_job = jobset.JobSpec(
357 cmdline=cmdline,
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700358 cwd=cwd,
359 environ=environ,
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700360 shortname="%s:%s:%s" % (suite_name, language, test_case),
Jan Tattermusch210a0ea2015-10-02 15:05:36 -0700361 timeout_seconds=2*60,
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700362 flake_retries=5 if args.allow_flakes else 0,
Jan Tattermusche2686282015-10-08 16:27:07 -0700363 timeout_retries=2 if args.allow_flakes else 0,
364 kill_handler=_job_kill_handler)
365 test_job.container_name = container_name
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700366 return test_job
367
Jan Tattermusch8266c672015-09-17 09:18:03 -0700368
369def cloud_to_cloud_jobspec(language, test_case, server_name, server_host,
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700370 server_port, docker_image=None):
Jan Tattermusch8266c672015-09-17 09:18:03 -0700371 """Creates jobspec for cloud-to-cloud interop test"""
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700372 cmdline = bash_login_cmdline(language.cloud_to_cloud_args() +
373 ['--test_case=%s' % test_case,
374 '--server_host=%s' % server_host,
375 '--server_port=%s' % server_port ])
376 cwd = language.client_cwd
377 if docker_image:
Jan Tattermusch0a14f622015-10-09 14:34:29 -0700378 container_name = dockerjob.random_name('interop_client_%s' % language.safename)
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700379 cmdline = docker_run_cmdline(cmdline,
380 image=docker_image,
381 cwd=cwd,
Jan Tattermusche2686282015-10-08 16:27:07 -0700382 docker_args=['--net=host',
383 '--name', container_name])
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700384 cwd = None
Jan Tattermusche2686282015-10-08 16:27:07 -0700385
Jan Tattermusch8266c672015-09-17 09:18:03 -0700386 test_job = jobset.JobSpec(
387 cmdline=cmdline,
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700388 cwd=cwd,
Jan Tattermusch8266c672015-09-17 09:18:03 -0700389 shortname="cloud_to_cloud:%s:%s_server:%s" % (language, server_name,
390 test_case),
Jan Tattermusch210a0ea2015-10-02 15:05:36 -0700391 timeout_seconds=2*60,
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700392 flake_retries=5 if args.allow_flakes else 0,
Jan Tattermusche2686282015-10-08 16:27:07 -0700393 timeout_retries=2 if args.allow_flakes else 0,
394 kill_handler=_job_kill_handler)
395 test_job.container_name = container_name
Jan Tattermusch8266c672015-09-17 09:18:03 -0700396 return test_job
397
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700398
399def server_jobspec(language, docker_image):
400 """Create jobspec for running a server"""
Jan Tattermusch0a14f622015-10-09 14:34:29 -0700401 container_name = dockerjob.random_name('interop_server_%s' % language.safename)
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700402 cmdline = bash_login_cmdline(language.server_args() +
403 ['--port=%s' % _DEFAULT_SERVER_PORT])
404 docker_cmdline = docker_run_cmdline(cmdline,
405 image=docker_image,
406 cwd=language.server_cwd,
407 docker_args=['-p', str(_DEFAULT_SERVER_PORT),
Jan Tattermusche2686282015-10-08 16:27:07 -0700408 '--name', container_name])
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700409 server_job = jobset.JobSpec(
410 cmdline=docker_cmdline,
Jan Tattermusche2686282015-10-08 16:27:07 -0700411 shortname="interop_server_%s" % language,
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700412 timeout_seconds=30*60)
Jan Tattermusche2686282015-10-08 16:27:07 -0700413 server_job.container_name = container_name
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700414 return server_job
415
416
417def build_interop_image_jobspec(language, tag=None):
418 """Creates jobspec for building interop docker image for a language"""
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700419 if not tag:
Jan Tattermusch0a14f622015-10-09 14:34:29 -0700420 tag = 'grpc_interop_%s:%s' % (language.safename, uuid.uuid4())
421 env = {'INTEROP_IMAGE': tag,
422 'BASE_NAME': 'grpc_interop_%s' % language.safename}
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700423 if not args.travis:
424 env['TTY_FLAG'] = '-t'
425 build_job = jobset.JobSpec(
426 cmdline=['tools/jenkins/build_interop_image.sh'],
427 environ=env,
428 shortname="build_docker_%s" % (language),
429 timeout_seconds=30*60)
430 build_job.tag = tag
431 return build_job
432
433
Jan Tattermusch320bd612015-09-15 12:44:35 -0700434argp = argparse.ArgumentParser(description='Run interop tests.')
435argp.add_argument('-l', '--language',
436 choices=['all'] + sorted(_LANGUAGES),
437 nargs='+',
Jan Tattermusch8266c672015-09-17 09:18:03 -0700438 default=['all'],
439 help='Clients to run.')
Jan Tattermusch210a0ea2015-10-02 15:05:36 -0700440argp.add_argument('-j', '--jobs', default=multiprocessing.cpu_count(), type=int)
Jan Tattermusch8266c672015-09-17 09:18:03 -0700441argp.add_argument('--cloud_to_prod',
442 default=False,
443 action='store_const',
444 const=True,
445 help='Run cloud_to_prod tests.')
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700446argp.add_argument('--cloud_to_prod_auth',
447 default=False,
448 action='store_const',
449 const=True,
450 help='Run cloud_to_prod_auth tests.')
Jan Tattermusch8266c672015-09-17 09:18:03 -0700451argp.add_argument('-s', '--server',
452 choices=['all'] + sorted(_SERVERS),
453 action='append',
454 help='Run cloud_to_cloud servers in a separate docker ' +
455 'image. Servers can only be started automatically if ' +
456 '--use_docker option is enabled.',
457 default=[])
458argp.add_argument('--override_server',
459 action='append',
460 type=lambda kv: kv.split("="),
461 help='Use servername=HOST:PORT to explicitly specify a server. E.g. csharp=localhost:50000',
462 default=[])
463argp.add_argument('-t', '--travis',
464 default=False,
465 action='store_const',
466 const=True)
467argp.add_argument('--use_docker',
468 default=False,
469 action='store_const',
470 const=True,
471 help='Run all the interop tests under docker. That provides ' +
472 'additional isolation and prevents the need to install ' +
473 'language specific prerequisites. Only available on Linux.')
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700474argp.add_argument('--allow_flakes',
475 default=False,
476 action='store_const',
477 const=True,
478 help="Allow flaky tests to show as passing (re-runs failed tests up to five times)")
Jan Tattermusch320bd612015-09-15 12:44:35 -0700479args = argp.parse_args()
480
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700481servers = set(s for s in itertools.chain.from_iterable(_SERVERS
Jan Tattermusch8266c672015-09-17 09:18:03 -0700482 if x == 'all' else [x]
483 for x in args.server))
484
485if args.use_docker:
486 if not args.travis:
487 print 'Seen --use_docker flag, will run interop tests under docker.'
488 print
489 print 'IMPORTANT: The changes you are testing need to be locally committed'
490 print 'because only the committed changes in the current branch will be'
491 print 'copied to the docker environment.'
492 time.sleep(5)
493
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700494if not args.use_docker and servers:
495 print "Running interop servers is only supported with --use_docker option enabled."
496 sys.exit(1)
Jan Tattermusch8266c672015-09-17 09:18:03 -0700497
Jan Tattermuschf49936a2015-09-16 15:44:26 -0700498languages = set(_LANGUAGES[l]
499 for l in itertools.chain.from_iterable(
500 _LANGUAGES.iterkeys() if x == 'all' else [x]
501 for x in args.language))
Jan Tattermusch320bd612015-09-15 12:44:35 -0700502
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700503docker_images={}
504if args.use_docker:
505 # languages for which to build docker images
506 languages_to_build = set(_LANGUAGES[k] for k in set([str(l) for l in languages] +
507 [s for s in servers]))
Jan Tattermusch8266c672015-09-17 09:18:03 -0700508
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700509 build_jobs = []
510 for l in languages_to_build:
511 job = build_interop_image_jobspec(l)
512 docker_images[str(l)] = job.tag
513 build_jobs.append(job)
Jan Tattermusch8266c672015-09-17 09:18:03 -0700514
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700515 if build_jobs:
516 jobset.message('START', 'Building interop docker images.', do_newline=True)
517 if jobset.run(build_jobs, newline_on_success=True, maxjobs=args.jobs):
518 jobset.message('SUCCESS', 'All docker images built successfully.', do_newline=True)
519 else:
520 jobset.message('FAILED', 'Failed to build interop docker images.', do_newline=True)
521 for image in docker_images.itervalues():
522 dockerjob.remove_image(image, skip_nonexistent=True)
523 exit(1);
Jan Tattermusch8266c672015-09-17 09:18:03 -0700524
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700525# Start interop servers.
526server_jobs={}
527server_addresses={}
528try:
529 for s in servers:
530 lang = str(s)
531 spec = server_jobspec(_LANGUAGES[lang], docker_images.get(lang))
532 job = dockerjob.DockerJob(spec)
533 server_jobs[lang] = job
534 server_addresses[lang] = ('localhost', job.mapped_port(_DEFAULT_SERVER_PORT))
Jan Tattermusch8266c672015-09-17 09:18:03 -0700535
Jan Tattermusch210a0ea2015-10-02 15:05:36 -0700536
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700537 jobs = []
538 if args.cloud_to_prod:
539 for language in languages:
540 for test_case in _TEST_CASES:
541 test_job = cloud_to_prod_jobspec(language, test_case,
542 docker_image=docker_images.get(str(language)))
543 jobs.append(test_job)
Jan Tattermusch320bd612015-09-15 12:44:35 -0700544
Jan Tattermuschfb8c77d2015-10-06 09:33:37 -0700545 if args.cloud_to_prod_auth:
546 for language in languages:
547 for test_case in _AUTH_TEST_CASES:
548 test_job = cloud_to_prod_jobspec(language, test_case,
549 docker_image=docker_images.get(str(language)),
550 auth=True)
551 jobs.append(test_job)
552
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700553 for server in args.override_server:
554 server_name = server[0]
555 (server_host, server_port) = server[1].split(':')
556 server_addresses[server_name] = (server_host, server_port)
Jan Tattermusch320bd612015-09-15 12:44:35 -0700557
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700558 for server_name, server_address in server_addresses.iteritems():
559 (server_host, server_port) = server_address
560 for language in languages:
561 for test_case in _TEST_CASES:
562 test_job = cloud_to_cloud_jobspec(language,
563 test_case,
564 server_name,
565 server_host,
566 server_port,
567 docker_image=docker_images.get(str(language)))
568 jobs.append(test_job)
Jan Tattermusch320bd612015-09-15 12:44:35 -0700569
Jan Tattermusch91ad0182015-10-01 09:22:03 -0700570 if not jobs:
571 print "No jobs to run."
572 for image in docker_images.itervalues():
573 dockerjob.remove_image(image, skip_nonexistent=True)
574 sys.exit(1)
575
576 root = ET.Element('testsuites')
577 testsuite = ET.SubElement(root, 'testsuite', id='1', package='grpc', name='tests')
578
579 if jobset.run(jobs, newline_on_success=True, maxjobs=args.jobs, xml_report=testsuite):
580 jobset.message('SUCCESS', 'All tests passed', do_newline=True)
581 else:
582 jobset.message('FAILED', 'Some tests failed', do_newline=True)
583
584 tree = ET.ElementTree(root)
585 tree.write('report.xml', encoding='UTF-8')
586
587finally:
588 # Check if servers are still running.
589 for server, job in server_jobs.iteritems():
590 if not job.is_running():
591 print 'Server "%s" has exited prematurely.' % server
592
593 dockerjob.finish_jobs([j for j in server_jobs.itervalues()])
594
595 for image in docker_images.itervalues():
596 print 'Removing docker image %s' % image
597 dockerjob.remove_image(image)