1# Copyright 2023 The gRPC Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15""" 16Contains macros used for running bazelified tests. 17""" 18 19load(":dockerimage_current_versions.bzl", "DOCKERIMAGE_CURRENT_VERSIONS") 20load("@bazel_toolchains//rules/exec_properties:exec_properties.bzl", "create_rbe_exec_properties_dict") 21 22def _dockerized_sh_test(name, srcs = [], args = [], data = [], size = "medium", timeout = None, tags = [], exec_compatible_with = [], flaky = None, docker_image_version = None, docker_run_as_root = False, env = {}): 23 """Runs sh_test under docker either via RBE or via docker sandbox.""" 24 if docker_image_version: 25 image_spec = DOCKERIMAGE_CURRENT_VERSIONS.get(docker_image_version, None) 26 if not image_spec: 27 fail("Version info for docker image '%s' not found in dockerimage_current_versions.bzl" % docker_image_version) 28 else: 29 fail("docker_image_version attribute not set for dockerized test '%s'" % name) 30 31 exec_properties = create_rbe_exec_properties_dict( 32 labels = { 33 "workload": "misc", 34 "machine_size": "misc_large", 35 }, 36 docker_network = "standard", 37 container_image = image_spec, 38 # TODO(jtattermusch): note that docker sandbox doesn't currently support "docker_run_as_root" 39 docker_run_as_root = docker_run_as_root, 40 ) 41 42 # since the tests require special bazel args, only run them when explicitly requested 43 tags = ["manual"] + tags 44 45 # TODO(jtattermusch): find a way to ensure that action can only run under docker sandbox or remotely 46 # to avoid running it outside of a docker container by accident. 47 48 test_args = { 49 "name": name, 50 "srcs": srcs, 51 "tags": tags, 52 "args": args, 53 "flaky": flaky, 54 "data": data, 55 "size": size, 56 "env": env, 57 "timeout": timeout, 58 "exec_compatible_with": exec_compatible_with, 59 "exec_properties": exec_properties, 60 } 61 62 native.sh_test( 63 **test_args 64 ) 65 66def _dockerized_genrule(name, cmd, outs, srcs = [], tags = [], exec_compatible_with = [], docker_image_version = None, docker_run_as_root = False): 67 """Runs genrule under docker either via RBE or via docker sandbox.""" 68 if docker_image_version: 69 image_spec = DOCKERIMAGE_CURRENT_VERSIONS.get(docker_image_version, None) 70 if not image_spec: 71 fail("Version info for docker image '%s' not found in dockerimage_current_versions.bzl" % docker_image_version) 72 else: 73 fail("docker_image_version attribute not set for dockerized test '%s'" % name) 74 75 exec_properties = create_rbe_exec_properties_dict( 76 labels = { 77 "workload": "misc", 78 "machine_size": "misc_large", 79 }, 80 docker_network = "standard", 81 container_image = image_spec, 82 # TODO(jtattermusch): note that docker sandbox doesn't currently support "docker_run_as_root" 83 docker_run_as_root = docker_run_as_root, 84 ) 85 86 # since the tests require special bazel args, only run them when explicitly requested 87 tags = ["manual"] + tags 88 89 # TODO(jtattermusch): find a way to ensure that action can only run under docker sandbox or remotely 90 # to avoid running it outside of a docker container by accident. 91 92 genrule_args = { 93 "name": name, 94 "cmd": cmd, 95 "srcs": srcs, 96 "tags": tags, 97 "exec_compatible_with": exec_compatible_with, 98 "exec_properties": exec_properties, 99 "outs": outs, 100 } 101 102 native.genrule( 103 **genrule_args 104 ) 105 106def grpc_run_tests_harness_test(name, args = [], data = [], size = "medium", timeout = None, tags = [], exec_compatible_with = [], flaky = None, docker_image_version = None, use_login_shell = None, prepare_script = None): 107 """Execute an run_tests.py-harness style test under bazel. 108 109 Args: 110 name: The name of the test. 111 args: The args to supply to the test binary. 112 data: Data dependencies. 113 size: The size of the test. 114 timeout: The test timeout. 115 tags: The tags for the test. 116 exec_compatible_with: A list of constraint values that must be 117 satisifed for the platform. 118 flaky: Whether this test is flaky. 119 docker_image_version: The docker .current_version file to use for docker containerization. 120 use_login_shell: If True, the run_tests.py command will run under a login shell. 121 prepare_script: Optional script that will be sourced before run_tests.py runs. 122 """ 123 124 data = [ 125 "//tools/bazelify_tests:grpc_repo_archive_with_submodules.tar.gz", 126 ] + data 127 128 args = [ 129 "$(location //tools/bazelify_tests:grpc_repo_archive_with_submodules.tar.gz)", 130 ] + args 131 132 srcs = [ 133 "//tools/bazelify_tests:grpc_run_tests_harness_test.sh", 134 ] 135 136 env = {} 137 138 if use_login_shell: 139 env["GRPC_RUNTESTS_USE_LOGIN_SHELL"] = "1" 140 141 if prepare_script: 142 data = data + [prepare_script] 143 env["GRPC_RUNTESTS_PREPARE_SCRIPT"] = "$(location " + prepare_script + ")" 144 145 # Enable ccache by default. This is important for speeding up the C++ cmake build, 146 # which isn't very efficient and tends to recompile some source files multiple times. 147 # Even though only the local disk cache is enabled (local to the docker container, 148 # so will be thrown away after the bazel actions finishes), ccache still speeds up 149 # the C++ build significantly. 150 # TODO(jtattermusch): find a cleaner way to toggle ccache for builds. 151 env["GRPC_BUILD_ENABLE_CCACHE"] = "true" 152 153 _dockerized_sh_test(name = name, srcs = srcs, args = args, data = data, size = size, timeout = timeout, tags = tags, exec_compatible_with = exec_compatible_with, flaky = flaky, docker_image_version = docker_image_version, env = env) 154 155def grpc_run_bazel_distribtest_test(name, args = [], data = [], size = "medium", timeout = None, tags = [], exec_compatible_with = [], flaky = None, docker_image_version = None): 156 """Execute bazel distribtest under bazel (an entire bazel build/test will run in a container as a single bazel action) 157 158 Args: 159 name: The name of the test. 160 args: The args to supply to the test binary. 161 data: Data dependencies. 162 size: The size of the test. 163 timeout: The test timeout. 164 tags: The tags for the test. 165 exec_compatible_with: A list of constraint values that must be 166 satisifed for the platform. 167 flaky: Whether this test is flaky. 168 docker_image_version: The docker .current_version file to use for docker containerization. 169 """ 170 171 data = [ 172 "//tools/bazelify_tests:grpc_repo_archive_with_submodules.tar.gz", 173 ] + data 174 175 args = [ 176 "$(location //tools/bazelify_tests:grpc_repo_archive_with_submodules.tar.gz)", 177 ] + args 178 179 srcs = [ 180 "//tools/bazelify_tests:grpc_run_bazel_distribtest_test.sh", 181 ] 182 env = {} 183 _dockerized_sh_test(name = name, srcs = srcs, args = args, data = data, size = size, timeout = timeout, tags = tags, exec_compatible_with = exec_compatible_with, flaky = flaky, docker_image_version = docker_image_version, env = env) 184 185def grpc_run_cpp_distribtest_test(name, args = [], data = [], size = "medium", timeout = None, tags = [], exec_compatible_with = [], flaky = None, docker_image_version = None): 186 """Execute an C++ distribtest under bazel. 187 188 Args: 189 name: The name of the test. 190 args: The args to supply to the test binary. 191 data: Data dependencies. 192 size: The size of the test. 193 timeout: The test timeout. 194 tags: The tags for the test. 195 exec_compatible_with: A list of constraint values that must be 196 satisifed for the platform. 197 flaky: Whether this test is flaky. 198 docker_image_version: The docker .current_version file to use for docker containerization. 199 """ 200 201 data = [ 202 "//tools/bazelify_tests:grpc_repo_archive_with_submodules.tar.gz", 203 ] + data 204 205 args = [ 206 "$(location //tools/bazelify_tests:grpc_repo_archive_with_submodules.tar.gz)", 207 ] + args 208 209 srcs = [ 210 "//tools/bazelify_tests:grpc_run_cpp_distribtest_test.sh", 211 ] 212 213 # TODO(jtattermusch): revisit running docker as root (but currently some distribtests need to install stuff inside the docker container) 214 env = {} 215 _dockerized_sh_test(name = name, srcs = srcs, args = args, data = data, size = size, timeout = timeout, tags = tags, exec_compatible_with = exec_compatible_with, flaky = flaky, docker_image_version = docker_image_version, env = env, docker_run_as_root = True) 216 217def grpc_run_simple_command_test(name, args = [], data = [], size = "medium", timeout = None, tags = [], exec_compatible_with = [], flaky = None, docker_image_version = None): 218 """Execute the specified test command under grpc workspace (and under a docker container) 219 220 Args: 221 name: The name of the test. 222 args: The command to run. 223 data: Data dependencies. 224 size: The size of the test. 225 timeout: The test timeout. 226 tags: The tags for the test. 227 exec_compatible_with: A list of constraint values that must be 228 satisifed for the platform. 229 flaky: Whether this test is flaky. 230 docker_image_version: The docker .current_version file to use for docker containerization. 231 """ 232 233 data = [ 234 "//tools/bazelify_tests:grpc_repo_archive_with_submodules.tar.gz", 235 ] + data 236 237 args = [ 238 "$(location //tools/bazelify_tests:grpc_repo_archive_with_submodules.tar.gz)", 239 ] + args 240 241 srcs = [ 242 "//tools/bazelify_tests:grpc_run_simple_command_test.sh", 243 ] 244 245 env = {} 246 _dockerized_sh_test(name = name, srcs = srcs, args = args, data = data, size = size, timeout = timeout, tags = tags, exec_compatible_with = exec_compatible_with, flaky = flaky, docker_image_version = docker_image_version, env = env, docker_run_as_root = False) 247 248def grpc_build_artifact_task(name, timeout = None, artifact_deps = [], tags = [], exec_compatible_with = [], flaky = None, docker_image_version = None, build_script = None): 249 """Execute a build artifact task and a corresponding 'build test'. 250 251 The artifact is built by a genrule that always succeeds (Even if the underlying build fails) 252 and an sh_test (with "_build_test" suffix) that presents the result of the artifact build 253 in the result UI (by displaying the the build status, the log, and artifacts produced). 254 Such layout helps to easily build artifacts and run distribtests that depend on other artifacts, 255 while making the test results well structured and easy to interpret. 256 257 Args: 258 name: The name of the target. 259 timeout: The test timeout for the build. 260 artifact_deps: List of dependencies on artifacts built by another grpc_build_artifact_task. 261 tags: The tags for the target. 262 exec_compatible_with: A list of constraint values that must be 263 satisifed for the platform. 264 flaky: Whether this artifact build is flaky. 265 docker_image_version: The docker .current_version file to use for docker containerization. 266 build_script: The script that builds the aritfacts. 267 """ 268 269 out_exitcode_file = str(name + "_exit_code") 270 out_build_log = str(name + "_build_log.txt") 271 out_archive_name = str(name + ".tar.gz") 272 273 genrule_outs = [ 274 out_exitcode_file, 275 out_build_log, 276 out_archive_name, 277 ] 278 279 genrule_srcs = [ 280 "//tools/bazelify_tests:grpc_build_artifact_task.sh", 281 "//tools/bazelify_tests:grpc_repo_archive_with_submodules.tar.gz", 282 build_script, 283 ] 284 285 cmd = "$(location //tools/bazelify_tests:grpc_build_artifact_task.sh) $(location //tools/bazelify_tests:grpc_repo_archive_with_submodules.tar.gz) $(location " + build_script + ") $(location " + out_exitcode_file + ") $(location " + out_build_log + ") $(location " + out_archive_name + ")" 286 287 # for each artifact task we depends on, use the correponding tar.gz as extra src and pass its location as an extra cmdline arg. 288 for dep in artifact_deps: 289 dep_archive_name = str(dep + ".tar.gz") 290 cmd = cmd + " $(location " + dep_archive_name + ")" 291 genrule_srcs.append(dep_archive_name) 292 293 _dockerized_genrule(name = name, cmd = cmd, outs = genrule_outs, srcs = genrule_srcs, tags = tags, exec_compatible_with = exec_compatible_with, docker_image_version = docker_image_version, docker_run_as_root = False) 294 295 # The genrule above always succeeds (even if the underlying build fails), so that we can create rules that depend 296 # on multiple artifact builds (of which some can fail). The actual build status (exitcode) and the log of the build 297 # will be reported by an associated sh_test (that gets displayed in the UI in a much nicer way than a genrule). 298 # Note that in bazel you cannot declare a test that has declared outputs and you also cannot make other rules 299 # depend on a test - which is the reason why we need a separate genrule to represent the build itself. 300 test_name = str(name + "_build_test") 301 test_srcs = [ 302 "//tools/bazelify_tests:grpc_build_artifact_task_build_test.sh", 303 ] 304 test_data = [ 305 out_exitcode_file, 306 out_build_log, 307 out_archive_name, 308 ] 309 test_env = {} 310 test_args = [ 311 "$(location " + out_exitcode_file + ")", 312 "$(location " + out_build_log + ")", 313 "$(location " + out_archive_name + ")", 314 ] 315 _dockerized_sh_test(name = test_name, srcs = test_srcs, args = test_args, data = test_data, size = "small", tags = tags, exec_compatible_with = exec_compatible_with, flaky = flaky, docker_image_version = docker_image_version, env = test_env, docker_run_as_root = False) 316 317def grpc_run_distribtest_test(name, artifact_deps = [], size = "medium", timeout = None, tags = [], exec_compatible_with = [], flaky = None, docker_image_version = None, build_script = None, docker_run_as_root = False): 318 """Run a distribtest for a previously built artifact/package 319 320 Args: 321 name: The name of the test. 322 artifact_deps: List of dependencies on artifacts built by another grpc_build_artifact_task. 323 size: The size of the test. 324 timeout: The test timeout. 325 tags: The tags for the test. 326 exec_compatible_with: A list of constraint values that must be 327 satisifed for the platform. 328 flaky: Whether this test is flaky. 329 docker_image_version: The docker .current_version file to use for docker containerization. 330 build_script: The script that runs the test. 331 docker_run_as_root: If True, the test will run under docker as root. 332 """ 333 334 data = [ 335 "//tools/bazelify_tests:grpc_repo_archive_with_submodules.tar.gz", 336 build_script, 337 ] 338 339 args = [ 340 "$(location //tools/bazelify_tests:grpc_repo_archive_with_submodules.tar.gz)", 341 "$(location " + build_script + ")", 342 ] 343 344 # for each artifact task we depends on, use the correponding tar.gz as extra data item and pass its location as an extra arg. 345 for dep in artifact_deps: 346 dep_archive_name = str(dep + ".tar.gz") 347 args.append("$(location " + dep_archive_name + ")") 348 data.append(dep_archive_name) 349 350 srcs = [ 351 "//tools/bazelify_tests:grpc_run_distribtest_test.sh", 352 ] 353 354 env = {} 355 _dockerized_sh_test(name = name, srcs = srcs, args = args, data = data, size = size, timeout = timeout, tags = tags, exec_compatible_with = exec_compatible_with, flaky = flaky, docker_image_version = docker_image_version, env = env, docker_run_as_root = docker_run_as_root) 356