| #!/usr/bin/env python3 | 
 | # Copyright (C) 2019 The Android Open Source Project | 
 | # | 
 | # Licensed under the Apache License, Version 2.0 (the "License"); | 
 | # you may not use this file except in compliance with the License. | 
 | # You may obtain a copy of the License at | 
 | # | 
 | #      http://www.apache.org/licenses/LICENSE-2.0 | 
 | # | 
 | # Unless required by applicable law or agreed to in writing, software | 
 | # distributed under the License is distributed on an "AS IS" BASIS, | 
 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | # See the License for the specific language governing permissions and | 
 | # limitations under the License. | 
 |  | 
 | from __future__ import print_function | 
 | import argparse | 
 | import distutils | 
 | import errno | 
 | import grp | 
 | import os | 
 | import readline | 
 | import sys | 
 | import shutil | 
 | import subprocess | 
 | from pipes import quote | 
 | from subprocess import check_call | 
 |  | 
 | try: | 
 |   from shutil import which as find_executable | 
 | except AttributeError: | 
 |   from distutils.spawn import find_executable | 
 |  | 
 | REPO_ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) | 
 | sys.path.append(os.path.join(REPO_ROOT, 'infra', 'ci')) | 
 | from config import JOB_CONFIGS, SANDBOX_IMG | 
 |  | 
 | try: | 
 |   input = raw_input | 
 | except NameError: | 
 |   pass | 
 |  | 
 |  | 
 | def user_in_docker_group(): | 
 |   try: | 
 |     group = grp.getgrnam('docker') | 
 |   except KeyError: | 
 |     return False | 
 |   else: | 
 |     return group.gr_gid in os.getgroups() | 
 |  | 
 |  | 
 | def decision(question='Would you like to continue', confirm=True, default='n'): | 
 |   default = default.lower().strip() | 
 |   yes = default in {'y', 'yes'} | 
 |   no = default in {'n', 'no'} | 
 |   default = 'y' if yes else 'n' | 
 |   prompt = '%s? [%s/%s]: ' % (question, 'Y' if yes else 'y', 'N' if no else 'n') | 
 |   if not confirm: | 
 |     print('%sy' % prompt) | 
 |     return | 
 |   while True: | 
 |     choice = input(prompt).lower().strip() | 
 |     if not choice: | 
 |       choice = default | 
 |     if choice in {'y', 'yes'}: | 
 |       return | 
 |     elif choice in {'n', 'no'}: | 
 |       sys.exit(3) | 
 |  | 
 |  | 
 | def main(): | 
 |   parser = argparse.ArgumentParser( | 
 |       formatter_class=argparse.ArgumentDefaultsHelpFormatter) | 
 |   parser.add_argument('config', choices=JOB_CONFIGS.keys()) | 
 |   parser.add_argument( | 
 |       '--runner', | 
 |       help='The container runner executable to use', | 
 |       choices=('podman', 'docker'), | 
 |       default='podman' if find_executable('podman') else 'docker') | 
 |   parser.add_argument( | 
 |       '--build', | 
 |       action='store_true', | 
 |       help='Will perform a build of sandbox image') | 
 |   group = parser.add_mutually_exclusive_group() | 
 |   group.add_argument( | 
 |       '--confirm', | 
 |       action='store_true', | 
 |       default=True, | 
 |       help='User confirmation of decision prompts') | 
 |   group.add_argument( | 
 |       '--no-confirm', | 
 |       dest='confirm', | 
 |       action='store_false', | 
 |       help='Forces confirmation of decision prompts') | 
 |   args = parser.parse_args() | 
 |  | 
 |   # Check that the directory is clean. | 
 |   git_cmd = ['git', '-C', REPO_ROOT, 'status', '--porcelain'] | 
 |   modified_files = subprocess.check_output(git_cmd).decode() | 
 |   if modified_files: | 
 |     print('The current Git repo has modified/untracked files.') | 
 |     print('The sandboxed VM will fetch the HEAD of your current git repo.') | 
 |     print('This is probably not the state you want to be in.') | 
 |     print('I suggest you stop, commit and then re-run this script') | 
 |     print('Modified files:\n' + modified_files) | 
 |     decision('Do you know what you are doing', confirm=args.confirm) | 
 |  | 
 |   if args.build: | 
 |     print('') | 
 |     print('About to build %r locally with %r' % (args.image, args.runner)) | 
 |     decision(confirm=args.confirm) | 
 |     check_call(('make', '-C', os.path.join(REPO_ROOT, 'infra', | 
 |                                            'ci'), | 
 |                 'BUILDER=%s' % args.runner, 'build-sandbox')) | 
 |  | 
 |   bundle_path = '/tmp/perfetto-ci.bundle' | 
 |   check_call(['git', '-C', REPO_ROOT, 'bundle', 'create', bundle_path, 'HEAD' ]) | 
 |   os.chmod(bundle_path, 0o664) | 
 |   env = { | 
 |       'PERFETTO_TEST_GIT_REF': bundle_path, | 
 |   } | 
 |   env.update(JOB_CONFIGS[args.config]) | 
 |  | 
 |   workdir = os.path.join(REPO_ROOT, 'out', 'tmp.ci') | 
 |   cmd = [] | 
 |   if args.runner == 'docker' and not user_in_docker_group(): | 
 |     cmd += ['sudo', '--'] | 
 |   cmd += [ | 
 |       args.runner, 'run', '-it', '--name', 'perfetto_ci', '--cap-add', | 
 |       'SYS_PTRACE', '--rm', '--volume', | 
 |       '%s:/ci/ramdisk' % workdir, '--tmpfs', '/tmp:exec', | 
 |       '--volume=%s:%s:ro' % (bundle_path, bundle_path) | 
 |   ] | 
 |   for kv in env.items(): | 
 |     cmd += ['--env', '%s=%s' % kv] | 
 |   cmd += [SANDBOX_IMG] | 
 |   cmd += [ | 
 |       'bash', '-c', | 
 |       'cd /ci/ramdisk; bash /ci/init.sh || sudo -u perfetto -EH bash -i' | 
 |   ] | 
 |  | 
 |   print( | 
 |       'About to run\n', | 
 |       ' '.join('\n  ' + c if c.startswith('--') or c == 'bash' else quote(c) | 
 |                for c in cmd)) | 
 |   print('') | 
 |   print('The VM workdir /ci/ramdisk will be mounted into: %s' % workdir) | 
 |   print('The contents of %s will be deleted before starting the VM' % workdir) | 
 |   decision(confirm=args.confirm) | 
 |  | 
 |   try: | 
 |     shutil.rmtree(workdir) | 
 |   except EnvironmentError as e: | 
 |     if e.errno == errno.ENOENT: | 
 |       pass | 
 |     elif e.errno == errno.EACCES: | 
 |       print('') | 
 |       print('Removing previous volume %r' % workdir) | 
 |       check_call(('sudo', 'rm', '-r', quote(workdir))) | 
 |     else: | 
 |       raise | 
 |  | 
 |   os.makedirs(workdir) | 
 |   os.execvp(cmd[0], cmd) | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |   sys.exit(main()) |