blob: 29e4cea98bc41fba65c78f584e2bfcfcaf25c8a9 [file]
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import print_function
import argparse
import os
import platform
import shutil
import subprocess
import sys
import tempfile
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'--image', required=True, help='Image in which command will be executed.')
parser.add_argument(
'--config-file',
required=True,
help='Location of the Docker config file.')
parser.add_argument(
'--dir-map',
metavar=('HOST_DIR', 'DOCKER_PATH'),
nargs=2,
action='append',
default=[],
help='Directories to be mapped in host_path:docker_path. Host paths '
'that do not exist will be created before running docker to make '
'sure that they are owned by current user.')
parser.add_argument(
'--env',
action='append',
default=[],
help='Environment variable strings, e.g. foo=bar')
parser.add_argument(
'--inherit-luci-context',
action='store_true',
default=False,
help='Inherit current LUCI Context (including auth). '
'CAUTION: removes network isolation between the container and the '
'docker host. Read more https://docs.docker.com/network/host/')
args, command = parser.parse_known_args()
if command and command[0] == '--':
command = command[1:]
cmd = [
'docker',
'--config',
args.config_file,
'run',
'--user',
'%s:%s' % (os.getuid(), os.getgid()),
]
for host_path, docker_path in args.dir_map:
# Ensure that host paths exist, otherwise they will be created by the docker
# command, which makes them owned by root and thus hard to remove/modify.
if not os.path.exists(host_path):
os.makedirs(host_path)
elif not os.path.isdir(host_path):
parser.error('Cannot map non-directory host path: %s' % host_path)
cmd.extend(['--volume', '%s:%s' % (host_path, docker_path)])
for var in args.env:
cmd.extend(['--env', var])
new_ctx_file = None
if args.inherit_luci_context:
if platform.system() != 'Linux':
print(
'--inherit-luci-context is supported only on Linux', file=sys.stderr)
return 1
cur_ctx_file = os.environ.get('LUCI_CONTEXT')
if not cur_ctx_file:
print('$LUCI_CONTEXT is not set or empty', file=sys.stderr)
return 1
# Copy the context file to make it available to Docker service.
new_ctx_file = tempfile.mktemp(prefix='luci_context')
shutil.copyfile(cur_ctx_file, new_ctx_file)
cmd.extend([
# Map the temp file to /tmp/luci_context inside the container.
'--volume',
'%s:/tmp/luci_context' % new_ctx_file,
# Add LUCI_CONTEXT variable pointing to /tmp/luci_context inside the
# container.
'--env',
'LUCI_CONTEXT=/tmp/luci_context',
# Remove network isolation, s.t. the container can contact local LUCI
# auth server.
'--network',
'host',
])
cmd.append(args.image)
cmd.extend(command)
try:
subprocess.check_call(cmd)
except subprocess.CalledProcessError as e:
return e.returncode
finally:
if new_ctx_file:
os.remove(new_ctx_file)
if __name__ == '__main__':
sys.exit(main())