1cb93a386Sopenharmony_ci# Copyright 2019 Google LLC 2cb93a386Sopenharmony_ci# 3cb93a386Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be 4cb93a386Sopenharmony_ci# found in the LICENSE file. 5cb93a386Sopenharmony_ci 6cb93a386Sopenharmony_ci 7cb93a386Sopenharmony_ciimport posixpath 8cb93a386Sopenharmony_cifrom recipe_engine import recipe_api 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci 11cb93a386Sopenharmony_ciMOUNT_SRC = '/SRC' 12cb93a386Sopenharmony_ciMOUNT_OUT = '/OUT' 13cb93a386Sopenharmony_ci 14cb93a386Sopenharmony_ci 15cb93a386Sopenharmony_ciclass DockerApi(recipe_api.RecipeApi): 16cb93a386Sopenharmony_ci def _chmod(self, filepath, mode, recursive=False): 17cb93a386Sopenharmony_ci cmd = ['chmod'] 18cb93a386Sopenharmony_ci if recursive: 19cb93a386Sopenharmony_ci cmd.append('-R') 20cb93a386Sopenharmony_ci cmd.extend([mode, filepath]) 21cb93a386Sopenharmony_ci name = ' '.join([str(elem) for elem in cmd]) 22cb93a386Sopenharmony_ci self.m.step(name, cmd=cmd, infra_step=True) 23cb93a386Sopenharmony_ci 24cb93a386Sopenharmony_ci def mount_src(self): 25cb93a386Sopenharmony_ci return MOUNT_SRC 26cb93a386Sopenharmony_ci 27cb93a386Sopenharmony_ci def mount_out(self): 28cb93a386Sopenharmony_ci return MOUNT_OUT 29cb93a386Sopenharmony_ci 30cb93a386Sopenharmony_ci # Unless match_directory_structure ==True, src_dir must be 31cb93a386Sopenharmony_ci # self.m.path['start_dir'] for the script to be located correctly. 32cb93a386Sopenharmony_ci def run(self, name, docker_image, src_dir, out_dir, script, args=None, docker_args=None, copies=None, recursive_read=None, attempts=1, match_directory_structure=False): 33cb93a386Sopenharmony_ci # Setup. Docker runs as a different user, so we need to give it access to 34cb93a386Sopenharmony_ci # read, write, and execute certain files. 35cb93a386Sopenharmony_ci with self.m.step.nest('Docker setup'): 36cb93a386Sopenharmony_ci step_stdout = self.m.python.inline( 37cb93a386Sopenharmony_ci name='Get uid and gid', 38cb93a386Sopenharmony_ci program='''import os 39cb93a386Sopenharmony_ciprint('%d:%d' % (os.getuid(), os.getgid())) 40cb93a386Sopenharmony_ci''', 41cb93a386Sopenharmony_ci stdout=self.m.raw_io.output(), 42cb93a386Sopenharmony_ci step_test_data=( 43cb93a386Sopenharmony_ci lambda: self.m.raw_io.test_api.stream_output('13:17')) 44cb93a386Sopenharmony_ci ).stdout.decode('utf-8') 45cb93a386Sopenharmony_ci uid_gid_pair = step_stdout.rstrip() if step_stdout else '' 46cb93a386Sopenharmony_ci # Make sure out_dir exists, otherwise mounting will fail. 47cb93a386Sopenharmony_ci # (Note that the docker --mount option, unlike the --volume option, does 48cb93a386Sopenharmony_ci # not create this dir as root if it doesn't exist.) 49cb93a386Sopenharmony_ci self.m.file.ensure_directory('mkdirs out_dir', out_dir, mode=0o777) 50cb93a386Sopenharmony_ci # ensure_directory won't change the permissions if the dir already exists, 51cb93a386Sopenharmony_ci # so we need to do that explicitly. 52cb93a386Sopenharmony_ci self._chmod(out_dir, '777') 53cb93a386Sopenharmony_ci 54cb93a386Sopenharmony_ci # chmod the src_dir, but not recursively; Swarming writes some files which 55cb93a386Sopenharmony_ci # we can't access, so "chmod -R" will fail if this is the root workdir. 56cb93a386Sopenharmony_ci self._chmod(src_dir, '755') 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_ci # Need to make the script executable, or Docker can't run it. 59cb93a386Sopenharmony_ci self._chmod(script, '0755') 60cb93a386Sopenharmony_ci 61cb93a386Sopenharmony_ci # Copy any requested files. 62cb93a386Sopenharmony_ci if copies: 63cb93a386Sopenharmony_ci for copy in copies: 64cb93a386Sopenharmony_ci src = copy['src'] 65cb93a386Sopenharmony_ci dest = copy['dst'] 66cb93a386Sopenharmony_ci dirname = self.m.path.dirname(dest) 67cb93a386Sopenharmony_ci self.m.file.ensure_directory( 68cb93a386Sopenharmony_ci 'mkdirs %s' % dirname, dirname, mode=0o777) 69cb93a386Sopenharmony_ci self.m.file.copy('cp %s %s' % (src, dest), src, dest) 70cb93a386Sopenharmony_ci self._chmod(dest, '644') 71cb93a386Sopenharmony_ci 72cb93a386Sopenharmony_ci # Recursive chmod any requested directories. 73cb93a386Sopenharmony_ci if recursive_read: 74cb93a386Sopenharmony_ci for elem in recursive_read: 75cb93a386Sopenharmony_ci self._chmod(elem, 'a+r', recursive=True) 76cb93a386Sopenharmony_ci 77cb93a386Sopenharmony_ci # Run. 78cb93a386Sopenharmony_ci cmd = [ 79cb93a386Sopenharmony_ci 'docker', 'run', '--shm-size=2gb', '--rm', '--user', uid_gid_pair, 80cb93a386Sopenharmony_ci '--mount', 'type=bind,source=%s,target=%s' % 81cb93a386Sopenharmony_ci (src_dir, src_dir if match_directory_structure else MOUNT_SRC), 82cb93a386Sopenharmony_ci '--mount', 'type=bind,source=%s,target=%s' % 83cb93a386Sopenharmony_ci (out_dir, out_dir if match_directory_structure else MOUNT_OUT), 84cb93a386Sopenharmony_ci ] 85cb93a386Sopenharmony_ci if docker_args: 86cb93a386Sopenharmony_ci cmd.extend(docker_args) 87cb93a386Sopenharmony_ci if not match_directory_structure: 88cb93a386Sopenharmony_ci # This only works when src_dir == self.m.path['start_dir'] but that's our 89cb93a386Sopenharmony_ci # only use case for now. 90cb93a386Sopenharmony_ci script = MOUNT_SRC + '/' + posixpath.relpath(str(script), str(self.m.path['start_dir'])) 91cb93a386Sopenharmony_ci cmd.extend([docker_image, script]) 92cb93a386Sopenharmony_ci if args: 93cb93a386Sopenharmony_ci cmd.extend(args) 94cb93a386Sopenharmony_ci 95cb93a386Sopenharmony_ci env = {'DOCKER_CONFIG': '/home/chrome-bot/.docker'} 96cb93a386Sopenharmony_ci with self.m.env(env): 97cb93a386Sopenharmony_ci self.m.run.with_retry(self.m.step, name, attempts, cmd=cmd) 98