blob: 4f35def27c4c5384e3b9289ba2304ff045bc6e68 [file] [log] [blame]
#!/usr/bin/env python
# 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'))
env = {
'PERFETTO_TEST_GIT_REF': 'file:///ci/source',
}
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:/ci/source:ro' % REPO_ROOT
]
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 will have read-only acess to: %s, mounted as /ci/source' %
REPO_ROOT)
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())