1*6dbdd20aSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*6dbdd20aSAndroid Build Coastguard Worker# Copyright (C) 2019 The Android Open Source Project 3*6dbdd20aSAndroid Build Coastguard Worker# 4*6dbdd20aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 5*6dbdd20aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 6*6dbdd20aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 7*6dbdd20aSAndroid Build Coastguard Worker# 8*6dbdd20aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 9*6dbdd20aSAndroid Build Coastguard Worker# 10*6dbdd20aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*6dbdd20aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 12*6dbdd20aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*6dbdd20aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 14*6dbdd20aSAndroid Build Coastguard Worker# limitations under the License. 15*6dbdd20aSAndroid Build Coastguard Worker 16*6dbdd20aSAndroid Build Coastguard Workerfrom __future__ import print_function 17*6dbdd20aSAndroid Build Coastguard Workerimport argparse 18*6dbdd20aSAndroid Build Coastguard Workerimport distutils 19*6dbdd20aSAndroid Build Coastguard Workerimport errno 20*6dbdd20aSAndroid Build Coastguard Workerimport grp 21*6dbdd20aSAndroid Build Coastguard Workerimport os 22*6dbdd20aSAndroid Build Coastguard Workerimport readline 23*6dbdd20aSAndroid Build Coastguard Workerimport sys 24*6dbdd20aSAndroid Build Coastguard Workerimport shutil 25*6dbdd20aSAndroid Build Coastguard Workerimport subprocess 26*6dbdd20aSAndroid Build Coastguard Workerfrom pipes import quote 27*6dbdd20aSAndroid Build Coastguard Workerfrom subprocess import check_call 28*6dbdd20aSAndroid Build Coastguard Worker 29*6dbdd20aSAndroid Build Coastguard Workertry: 30*6dbdd20aSAndroid Build Coastguard Worker from shutil import which as find_executable 31*6dbdd20aSAndroid Build Coastguard Workerexcept AttributeError: 32*6dbdd20aSAndroid Build Coastguard Worker from distutils.spawn import find_executable 33*6dbdd20aSAndroid Build Coastguard Worker 34*6dbdd20aSAndroid Build Coastguard WorkerREPO_ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 35*6dbdd20aSAndroid Build Coastguard Workersys.path.append(os.path.join(REPO_ROOT, 'infra', 'ci')) 36*6dbdd20aSAndroid Build Coastguard Workerfrom config import JOB_CONFIGS, SANDBOX_IMG 37*6dbdd20aSAndroid Build Coastguard Worker 38*6dbdd20aSAndroid Build Coastguard Workertry: 39*6dbdd20aSAndroid Build Coastguard Worker input = raw_input 40*6dbdd20aSAndroid Build Coastguard Workerexcept NameError: 41*6dbdd20aSAndroid Build Coastguard Worker pass 42*6dbdd20aSAndroid Build Coastguard Worker 43*6dbdd20aSAndroid Build Coastguard Worker 44*6dbdd20aSAndroid Build Coastguard Workerdef user_in_docker_group(): 45*6dbdd20aSAndroid Build Coastguard Worker try: 46*6dbdd20aSAndroid Build Coastguard Worker group = grp.getgrnam('docker') 47*6dbdd20aSAndroid Build Coastguard Worker except KeyError: 48*6dbdd20aSAndroid Build Coastguard Worker return False 49*6dbdd20aSAndroid Build Coastguard Worker else: 50*6dbdd20aSAndroid Build Coastguard Worker return group.gr_gid in os.getgroups() 51*6dbdd20aSAndroid Build Coastguard Worker 52*6dbdd20aSAndroid Build Coastguard Worker 53*6dbdd20aSAndroid Build Coastguard Workerdef decision(question='Would you like to continue', confirm=True, default='n'): 54*6dbdd20aSAndroid Build Coastguard Worker default = default.lower().strip() 55*6dbdd20aSAndroid Build Coastguard Worker yes = default in {'y', 'yes'} 56*6dbdd20aSAndroid Build Coastguard Worker no = default in {'n', 'no'} 57*6dbdd20aSAndroid Build Coastguard Worker default = 'y' if yes else 'n' 58*6dbdd20aSAndroid Build Coastguard Worker prompt = '%s? [%s/%s]: ' % (question, 'Y' if yes else 'y', 'N' if no else 'n') 59*6dbdd20aSAndroid Build Coastguard Worker if not confirm: 60*6dbdd20aSAndroid Build Coastguard Worker print('%sy' % prompt) 61*6dbdd20aSAndroid Build Coastguard Worker return 62*6dbdd20aSAndroid Build Coastguard Worker while True: 63*6dbdd20aSAndroid Build Coastguard Worker choice = input(prompt).lower().strip() 64*6dbdd20aSAndroid Build Coastguard Worker if not choice: 65*6dbdd20aSAndroid Build Coastguard Worker choice = default 66*6dbdd20aSAndroid Build Coastguard Worker if choice in {'y', 'yes'}: 67*6dbdd20aSAndroid Build Coastguard Worker return 68*6dbdd20aSAndroid Build Coastguard Worker elif choice in {'n', 'no'}: 69*6dbdd20aSAndroid Build Coastguard Worker sys.exit(3) 70*6dbdd20aSAndroid Build Coastguard Worker 71*6dbdd20aSAndroid Build Coastguard Worker 72*6dbdd20aSAndroid Build Coastguard Workerdef main(): 73*6dbdd20aSAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 74*6dbdd20aSAndroid Build Coastguard Worker formatter_class=argparse.ArgumentDefaultsHelpFormatter) 75*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('config', choices=JOB_CONFIGS.keys()) 76*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument( 77*6dbdd20aSAndroid Build Coastguard Worker '--runner', 78*6dbdd20aSAndroid Build Coastguard Worker help='The container runner executable to use', 79*6dbdd20aSAndroid Build Coastguard Worker choices=('podman', 'docker'), 80*6dbdd20aSAndroid Build Coastguard Worker default='podman' if find_executable('podman') else 'docker') 81*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument( 82*6dbdd20aSAndroid Build Coastguard Worker '--build', 83*6dbdd20aSAndroid Build Coastguard Worker action='store_true', 84*6dbdd20aSAndroid Build Coastguard Worker help='Will perform a build of sandbox image') 85*6dbdd20aSAndroid Build Coastguard Worker group = parser.add_mutually_exclusive_group() 86*6dbdd20aSAndroid Build Coastguard Worker group.add_argument( 87*6dbdd20aSAndroid Build Coastguard Worker '--confirm', 88*6dbdd20aSAndroid Build Coastguard Worker action='store_true', 89*6dbdd20aSAndroid Build Coastguard Worker default=True, 90*6dbdd20aSAndroid Build Coastguard Worker help='User confirmation of decision prompts') 91*6dbdd20aSAndroid Build Coastguard Worker group.add_argument( 92*6dbdd20aSAndroid Build Coastguard Worker '--no-confirm', 93*6dbdd20aSAndroid Build Coastguard Worker dest='confirm', 94*6dbdd20aSAndroid Build Coastguard Worker action='store_false', 95*6dbdd20aSAndroid Build Coastguard Worker help='Forces confirmation of decision prompts') 96*6dbdd20aSAndroid Build Coastguard Worker args = parser.parse_args() 97*6dbdd20aSAndroid Build Coastguard Worker 98*6dbdd20aSAndroid Build Coastguard Worker # Check that the directory is clean. 99*6dbdd20aSAndroid Build Coastguard Worker git_cmd = ['git', '-C', REPO_ROOT, 'status', '--porcelain'] 100*6dbdd20aSAndroid Build Coastguard Worker modified_files = subprocess.check_output(git_cmd).decode() 101*6dbdd20aSAndroid Build Coastguard Worker if modified_files: 102*6dbdd20aSAndroid Build Coastguard Worker print('The current Git repo has modified/untracked files.') 103*6dbdd20aSAndroid Build Coastguard Worker print('The sandboxed VM will fetch the HEAD of your current git repo.') 104*6dbdd20aSAndroid Build Coastguard Worker print('This is probably not the state you want to be in.') 105*6dbdd20aSAndroid Build Coastguard Worker print('I suggest you stop, commit and then re-run this script') 106*6dbdd20aSAndroid Build Coastguard Worker print('Modified files:\n' + modified_files) 107*6dbdd20aSAndroid Build Coastguard Worker decision('Do you know what you are doing', confirm=args.confirm) 108*6dbdd20aSAndroid Build Coastguard Worker 109*6dbdd20aSAndroid Build Coastguard Worker if args.build: 110*6dbdd20aSAndroid Build Coastguard Worker print('') 111*6dbdd20aSAndroid Build Coastguard Worker print('About to build %r locally with %r' % (args.image, args.runner)) 112*6dbdd20aSAndroid Build Coastguard Worker decision(confirm=args.confirm) 113*6dbdd20aSAndroid Build Coastguard Worker check_call(('make', '-C', os.path.join(REPO_ROOT, 'infra', 'ci'), 114*6dbdd20aSAndroid Build Coastguard Worker 'BUILDER=%s' % args.runner, 'build-sandbox')) 115*6dbdd20aSAndroid Build Coastguard Worker 116*6dbdd20aSAndroid Build Coastguard Worker bundle_path = '/tmp/perfetto-ci.bundle' 117*6dbdd20aSAndroid Build Coastguard Worker check_call(['git', '-C', REPO_ROOT, 'bundle', 'create', bundle_path, 'HEAD']) 118*6dbdd20aSAndroid Build Coastguard Worker os.chmod(bundle_path, 0o664) 119*6dbdd20aSAndroid Build Coastguard Worker env = { 120*6dbdd20aSAndroid Build Coastguard Worker 'PERFETTO_TEST_GIT_REF': bundle_path, 121*6dbdd20aSAndroid Build Coastguard Worker } 122*6dbdd20aSAndroid Build Coastguard Worker env.update(JOB_CONFIGS[args.config]) 123*6dbdd20aSAndroid Build Coastguard Worker 124*6dbdd20aSAndroid Build Coastguard Worker workdir = os.path.join(REPO_ROOT, 'out', 'tmp.ci') 125*6dbdd20aSAndroid Build Coastguard Worker cmd = [] 126*6dbdd20aSAndroid Build Coastguard Worker if args.runner == 'docker' and not user_in_docker_group(): 127*6dbdd20aSAndroid Build Coastguard Worker cmd += ['sudo', '--'] 128*6dbdd20aSAndroid Build Coastguard Worker cmd += [ 129*6dbdd20aSAndroid Build Coastguard Worker args.runner, 'run', '-it', '--name', 'perfetto_ci', '--cap-add', 130*6dbdd20aSAndroid Build Coastguard Worker 'SYS_PTRACE', '--rm', '--volume', 131*6dbdd20aSAndroid Build Coastguard Worker '%s:/ci/ramdisk' % workdir, '--tmpfs', '/tmp:exec', 132*6dbdd20aSAndroid Build Coastguard Worker '--volume=%s:%s:ro' % (bundle_path, bundle_path) 133*6dbdd20aSAndroid Build Coastguard Worker ] 134*6dbdd20aSAndroid Build Coastguard Worker for kv in env.items(): 135*6dbdd20aSAndroid Build Coastguard Worker cmd += ['--env', '%s=%s' % kv] 136*6dbdd20aSAndroid Build Coastguard Worker cmd += [SANDBOX_IMG] 137*6dbdd20aSAndroid Build Coastguard Worker cmd += [ 138*6dbdd20aSAndroid Build Coastguard Worker 'bash', '-c', 139*6dbdd20aSAndroid Build Coastguard Worker 'cd /ci/ramdisk; bash /ci/init.sh || sudo -u perfetto -EH bash -i' 140*6dbdd20aSAndroid Build Coastguard Worker ] 141*6dbdd20aSAndroid Build Coastguard Worker 142*6dbdd20aSAndroid Build Coastguard Worker print( 143*6dbdd20aSAndroid Build Coastguard Worker 'About to run\n', 144*6dbdd20aSAndroid Build Coastguard Worker ' '.join('\n ' + c if c.startswith('--') or c == 'bash' else quote(c) 145*6dbdd20aSAndroid Build Coastguard Worker for c in cmd)) 146*6dbdd20aSAndroid Build Coastguard Worker print('') 147*6dbdd20aSAndroid Build Coastguard Worker print('The VM workdir /ci/ramdisk will be mounted into: %s' % workdir) 148*6dbdd20aSAndroid Build Coastguard Worker print('The contents of %s will be deleted before starting the VM' % workdir) 149*6dbdd20aSAndroid Build Coastguard Worker decision(confirm=args.confirm) 150*6dbdd20aSAndroid Build Coastguard Worker 151*6dbdd20aSAndroid Build Coastguard Worker try: 152*6dbdd20aSAndroid Build Coastguard Worker shutil.rmtree(workdir) 153*6dbdd20aSAndroid Build Coastguard Worker except EnvironmentError as e: 154*6dbdd20aSAndroid Build Coastguard Worker if e.errno == errno.ENOENT: 155*6dbdd20aSAndroid Build Coastguard Worker pass 156*6dbdd20aSAndroid Build Coastguard Worker elif e.errno == errno.EACCES: 157*6dbdd20aSAndroid Build Coastguard Worker print('') 158*6dbdd20aSAndroid Build Coastguard Worker print('Removing previous volume %r' % workdir) 159*6dbdd20aSAndroid Build Coastguard Worker check_call(('sudo', 'rm', '-r', quote(workdir))) 160*6dbdd20aSAndroid Build Coastguard Worker else: 161*6dbdd20aSAndroid Build Coastguard Worker raise 162*6dbdd20aSAndroid Build Coastguard Worker 163*6dbdd20aSAndroid Build Coastguard Worker os.makedirs(workdir) 164*6dbdd20aSAndroid Build Coastguard Worker os.execvp(cmd[0], cmd) 165*6dbdd20aSAndroid Build Coastguard Worker 166*6dbdd20aSAndroid Build Coastguard Worker 167*6dbdd20aSAndroid Build Coastguard Workerif __name__ == '__main__': 168*6dbdd20aSAndroid Build Coastguard Worker sys.exit(main()) 169