1#!/usr/bin/env python3
2# Copyright 2016 gRPC authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15"""Definition of targets run distribution package tests."""
16
17import os.path
18import sys
19
20sys.path.insert(0, os.path.abspath('..'))
21import python_utils.jobset as jobset
22
23
24def create_docker_jobspec(name,
25                          dockerfile_dir,
26                          shell_command,
27                          environ={},
28                          flake_retries=0,
29                          timeout_retries=0,
30                          copy_rel_path=None,
31                          timeout_seconds=30 * 60):
32    """Creates jobspec for a task running under docker."""
33    environ = environ.copy()
34    # the entire repo will be cloned if copy_rel_path is not set.
35    if copy_rel_path:
36        environ['RELATIVE_COPY_PATH'] = copy_rel_path
37
38    docker_args = []
39    for k, v in list(environ.items()):
40        docker_args += ['-e', '%s=%s' % (k, v)]
41    docker_env = {
42        'DOCKERFILE_DIR': dockerfile_dir,
43        'DOCKER_RUN_SCRIPT': 'tools/run_tests/dockerize/docker_run.sh',
44        'DOCKER_RUN_SCRIPT_COMMAND': shell_command,
45    }
46    jobspec = jobset.JobSpec(
47        cmdline=['tools/run_tests/dockerize/build_and_run_docker.sh'] +
48        docker_args,
49        environ=docker_env,
50        shortname='distribtest.%s' % (name),
51        timeout_seconds=timeout_seconds,
52        flake_retries=flake_retries,
53        timeout_retries=timeout_retries)
54    return jobspec
55
56
57def create_jobspec(name,
58                   cmdline,
59                   environ=None,
60                   shell=False,
61                   flake_retries=0,
62                   timeout_retries=0,
63                   use_workspace=False,
64                   timeout_seconds=10 * 60):
65    """Creates jobspec."""
66    environ = environ.copy()
67    if use_workspace:
68        environ['WORKSPACE_NAME'] = 'workspace_%s' % name
69        cmdline = ['bash', 'tools/run_tests/artifacts/run_in_workspace.sh'
70                  ] + cmdline
71    jobspec = jobset.JobSpec(cmdline=cmdline,
72                             environ=environ,
73                             shortname='distribtest.%s' % (name),
74                             timeout_seconds=timeout_seconds,
75                             flake_retries=flake_retries,
76                             timeout_retries=timeout_retries,
77                             shell=shell)
78    return jobspec
79
80
81class CSharpDistribTest(object):
82    """Tests C# NuGet package"""
83
84    def __init__(self,
85                 platform,
86                 arch,
87                 docker_suffix=None,
88                 use_dotnet_cli=False,
89                 presubmit=False):
90        self.name = 'csharp_%s_%s' % (platform, arch)
91        self.platform = platform
92        self.arch = arch
93        self.docker_suffix = docker_suffix
94        self.labels = ['distribtest', 'csharp', platform, arch]
95        if presubmit:
96            self.labels.append('presubmit')
97        self.script_suffix = ''
98        if docker_suffix:
99            self.name += '_%s' % docker_suffix
100            self.labels.append(docker_suffix)
101        if use_dotnet_cli:
102            self.name += '_dotnetcli'
103            self.script_suffix = '_dotnetcli'
104            self.labels.append('dotnetcli')
105        else:
106            self.labels.append('olddotnet')
107
108    def pre_build_jobspecs(self):
109        return []
110
111    def build_jobspec(self, inner_jobs=None):
112        del inner_jobs  # arg unused as there is little opportunity for parallelizing whats inside the distribtests
113        if self.platform == 'linux':
114            return create_docker_jobspec(
115                self.name,
116                'tools/dockerfile/distribtest/csharp_%s_%s' %
117                (self.docker_suffix, self.arch),
118                'test/distrib/csharp/run_distrib_test%s.sh' %
119                self.script_suffix,
120                copy_rel_path='test/distrib')
121        elif self.platform == 'macos':
122            return create_jobspec(self.name, [
123                'test/distrib/csharp/run_distrib_test%s.sh' % self.script_suffix
124            ],
125                                  environ={
126                                      'EXTERNAL_GIT_ROOT': '../../../..',
127                                      'SKIP_NETCOREAPP21_DISTRIBTEST': '1',
128                                      'SKIP_NET50_DISTRIBTEST': '1',
129                                  },
130                                  use_workspace=True)
131        elif self.platform == 'windows':
132            # TODO(jtattermusch): re-enable windows distribtest
133            return create_jobspec(
134                self.name,
135                ['bash', 'tools/run_tests/artifacts/run_distribtest_csharp.sh'],
136                environ={},
137                use_workspace=True)
138        else:
139            raise Exception("Not supported yet.")
140
141    def __str__(self):
142        return self.name
143
144
145class PythonDistribTest(object):
146    """Tests Python package"""
147
148    def __init__(self,
149                 platform,
150                 arch,
151                 docker_suffix,
152                 source=False,
153                 presubmit=False):
154        self.source = source
155        if source:
156            self.name = 'python_dev_%s_%s_%s' % (platform, arch, docker_suffix)
157        else:
158            self.name = 'python_%s_%s_%s' % (platform, arch, docker_suffix)
159        self.platform = platform
160        self.arch = arch
161        self.docker_suffix = docker_suffix
162        self.labels = ['distribtest', 'python', platform, arch, docker_suffix]
163        if presubmit:
164            self.labels.append('presubmit')
165
166    def pre_build_jobspecs(self):
167        return []
168
169    def build_jobspec(self, inner_jobs=None):
170        # TODO(jtattermusch): honor inner_jobs arg for this task.
171        del inner_jobs
172        if not self.platform == 'linux':
173            raise Exception("Not supported yet.")
174
175        if self.source:
176            return create_docker_jobspec(
177                self.name,
178                'tools/dockerfile/distribtest/python_dev_%s_%s' %
179                (self.docker_suffix, self.arch),
180                'test/distrib/python/run_source_distrib_test.sh',
181                copy_rel_path='test/distrib')
182        else:
183            return create_docker_jobspec(
184                self.name,
185                'tools/dockerfile/distribtest/python_%s_%s' %
186                (self.docker_suffix, self.arch),
187                'test/distrib/python/run_binary_distrib_test.sh',
188                copy_rel_path='test/distrib')
189
190    def __str__(self):
191        return self.name
192
193
194class RubyDistribTest(object):
195    """Tests Ruby package"""
196
197    def __init__(self,
198                 platform,
199                 arch,
200                 docker_suffix,
201                 ruby_version=None,
202                 source=False,
203                 presubmit=False):
204        self.package_type = 'binary'
205        if source:
206            self.package_type = 'source'
207        self.name = 'ruby_%s_%s_%s_version_%s_package_type_%s' % (
208            platform, arch, docker_suffix, ruby_version or
209            'unspecified', self.package_type)
210        self.platform = platform
211        self.arch = arch
212        self.docker_suffix = docker_suffix
213        self.ruby_version = ruby_version
214        self.labels = ['distribtest', 'ruby', platform, arch, docker_suffix]
215        if presubmit:
216            self.labels.append('presubmit')
217
218    def pre_build_jobspecs(self):
219        return []
220
221    def build_jobspec(self, inner_jobs=None):
222        # TODO(jtattermusch): honor inner_jobs arg for this task.
223        del inner_jobs
224        arch_to_gem_arch = {
225            'x64': 'x86_64',
226            'x86': 'x86',
227        }
228        if not self.platform == 'linux':
229            raise Exception("Not supported yet.")
230
231        dockerfile_name = 'tools/dockerfile/distribtest/ruby_%s_%s' % (
232            self.docker_suffix, self.arch)
233        if self.ruby_version is not None:
234            dockerfile_name += '_%s' % self.ruby_version
235        return create_docker_jobspec(
236            self.name,
237            dockerfile_name,
238            'test/distrib/ruby/run_distrib_test.sh %s %s %s' %
239            (arch_to_gem_arch[self.arch], self.platform, self.package_type),
240            copy_rel_path='test/distrib')
241
242    def __str__(self):
243        return self.name
244
245
246class PHP7DistribTest(object):
247    """Tests PHP7 package"""
248
249    def __init__(self, platform, arch, docker_suffix=None, presubmit=False):
250        self.name = 'php7_%s_%s_%s' % (platform, arch, docker_suffix)
251        self.platform = platform
252        self.arch = arch
253        self.docker_suffix = docker_suffix
254        self.labels = ['distribtest', 'php', 'php7', platform, arch]
255        if presubmit:
256            self.labels.append('presubmit')
257        if docker_suffix:
258            self.labels.append(docker_suffix)
259
260    def pre_build_jobspecs(self):
261        return []
262
263    def build_jobspec(self, inner_jobs=None):
264        # TODO(jtattermusch): honor inner_jobs arg for this task.
265        del inner_jobs
266        if self.platform == 'linux':
267            return create_docker_jobspec(
268                self.name,
269                'tools/dockerfile/distribtest/php7_%s_%s' %
270                (self.docker_suffix, self.arch),
271                'test/distrib/php/run_distrib_test.sh',
272                copy_rel_path='test/distrib')
273        elif self.platform == 'macos':
274            return create_jobspec(
275                self.name, ['test/distrib/php/run_distrib_test_macos.sh'],
276                environ={'EXTERNAL_GIT_ROOT': '../../../..'},
277                timeout_seconds=20 * 60,
278                use_workspace=True)
279        else:
280            raise Exception("Not supported yet.")
281
282    def __str__(self):
283        return self.name
284
285
286class CppDistribTest(object):
287    """Tests Cpp make install by building examples."""
288
289    def __init__(self,
290                 platform,
291                 arch,
292                 docker_suffix=None,
293                 testcase=None,
294                 presubmit=False):
295        if platform == 'linux':
296            self.name = 'cpp_%s_%s_%s_%s' % (platform, arch, docker_suffix,
297                                             testcase)
298        else:
299            self.name = 'cpp_%s_%s_%s' % (platform, arch, testcase)
300        self.platform = platform
301        self.arch = arch
302        self.docker_suffix = docker_suffix
303        self.testcase = testcase
304        self.labels = [
305            'distribtest',
306            'cpp',
307            platform,
308            arch,
309            testcase,
310        ]
311        if presubmit:
312            self.labels.append('presubmit')
313        if docker_suffix:
314            self.labels.append(docker_suffix)
315
316    def pre_build_jobspecs(self):
317        return []
318
319    def build_jobspec(self, inner_jobs=None):
320        environ = {}
321        if inner_jobs is not None:
322            # set number of parallel jobs for the C++ build
323            environ['GRPC_CPP_DISTRIBTEST_BUILD_COMPILER_JOBS'] = str(
324                inner_jobs)
325
326        if self.platform == 'linux':
327            return create_docker_jobspec(
328                self.name,
329                'tools/dockerfile/distribtest/cpp_%s_%s' %
330                (self.docker_suffix, self.arch),
331                'test/distrib/cpp/run_distrib_test_%s.sh' % self.testcase,
332                timeout_seconds=45 * 60)
333        elif self.platform == 'windows':
334            return create_jobspec(
335                self.name,
336                ['test\\distrib\\cpp\\run_distrib_test_%s.bat' % self.testcase],
337                environ={},
338                timeout_seconds=45 * 60,
339                use_workspace=True)
340        else:
341            raise Exception("Not supported yet.")
342
343    def __str__(self):
344        return self.name
345
346
347def targets():
348    """Gets list of supported targets"""
349    return [
350        # C++
351        CppDistribTest('linux', 'x64', 'debian10', 'cmake', presubmit=True),
352        CppDistribTest('linux',
353                       'x64',
354                       'debian10',
355                       'cmake_as_submodule',
356                       presubmit=True),
357        CppDistribTest('linux',
358                       'x64',
359                       'debian10',
360                       'cmake_as_externalproject',
361                       presubmit=True),
362        CppDistribTest('linux',
363                       'x64',
364                       'debian10',
365                       'cmake_fetchcontent',
366                       presubmit=True),
367        CppDistribTest('linux',
368                       'x64',
369                       'debian10',
370                       'cmake_module_install',
371                       presubmit=True),
372        CppDistribTest('linux',
373                       'x64',
374                       'debian10',
375                       'cmake_pkgconfig',
376                       presubmit=True),
377        CppDistribTest('linux',
378                       'x64',
379                       'debian10_aarch64_cross',
380                       'cmake_aarch64_cross',
381                       presubmit=True),
382        CppDistribTest('windows', 'x86', testcase='cmake', presubmit=True),
383        CppDistribTest('windows',
384                       'x86',
385                       testcase='cmake_as_externalproject',
386                       presubmit=True),
387        # C#
388        CSharpDistribTest('linux',
389                          'x64',
390                          'debian10',
391                          use_dotnet_cli=True,
392                          presubmit=True),
393        CSharpDistribTest('linux', 'x64', 'ubuntu1604', use_dotnet_cli=True),
394        CSharpDistribTest('linux',
395                          'x64',
396                          'alpine',
397                          use_dotnet_cli=True,
398                          presubmit=True),
399        CSharpDistribTest('linux',
400                          'x64',
401                          'dotnet31',
402                          use_dotnet_cli=True,
403                          presubmit=True),
404        CSharpDistribTest('linux',
405                          'x64',
406                          'dotnet5',
407                          use_dotnet_cli=True,
408                          presubmit=True),
409        CSharpDistribTest('macos', 'x64', use_dotnet_cli=True, presubmit=True),
410        CSharpDistribTest('windows', 'x86', presubmit=True),
411        CSharpDistribTest('windows', 'x64', presubmit=True),
412        # Python
413        PythonDistribTest('linux', 'x64', 'buster', presubmit=True),
414        PythonDistribTest('linux', 'x86', 'buster', presubmit=True),
415        PythonDistribTest('linux', 'x64', 'fedora34'),
416        PythonDistribTest('linux', 'x64', 'arch'),
417        PythonDistribTest('linux', 'x64', 'alpine'),
418        PythonDistribTest('linux', 'x64', 'ubuntu2004'),
419        PythonDistribTest('linux', 'aarch64', 'python38_buster',
420                          presubmit=True),
421        PythonDistribTest('linux',
422                          'x64',
423                          'alpine3.7',
424                          source=True,
425                          presubmit=True),
426        PythonDistribTest('linux', 'x64', 'buster', source=True,
427                          presubmit=True),
428        PythonDistribTest('linux', 'x86', 'buster', source=True,
429                          presubmit=True),
430        PythonDistribTest('linux', 'x64', 'fedora34', source=True),
431        PythonDistribTest('linux', 'x64', 'arch', source=True),
432        PythonDistribTest('linux', 'x64', 'ubuntu2004', source=True),
433        # Ruby
434        RubyDistribTest('linux',
435                        'x64',
436                        'debian10',
437                        ruby_version='ruby_2_6',
438                        source=True,
439                        presubmit=True),
440        RubyDistribTest('linux',
441                        'x64',
442                        'debian10',
443                        ruby_version='ruby_2_7',
444                        presubmit=True),
445        RubyDistribTest('linux', 'x64', 'centos7'),
446        RubyDistribTest('linux', 'x64', 'ubuntu1604'),
447        RubyDistribTest('linux', 'x64', 'ubuntu1804', presubmit=True),
448        # PHP7
449        PHP7DistribTest('linux', 'x64', 'debian10', presubmit=True),
450        PHP7DistribTest('macos', 'x64', presubmit=True),
451    ]
452