Bug 1432390 - Make `mach taskcluster-build-image` talk directly to the docker socket in the image builder. r?dustin
Now that `mach taskcluster-build-image` can, we can avoid all the manual
handling based on curl and jq in the image builder.
An additional advantage on relying on `mach taskcluster-build-image`
doing more is that less changes to the build-image.sh script will be
necessary, and thus less updates of the image builder docker image.
--- a/taskcluster/docker/image_builder/build-image.sh
+++ b/taskcluster/docker/image_builder/build-image.sh
@@ -13,55 +13,35 @@ raise_error() {
exit 1
}
# Ensure that the PROJECT is specified so the image can be indexed
test -n "$PROJECT" || raise_error "PROJECT must be provided."
test -n "$HASH" || raise_error "Context HASH must be provided."
test -n "$IMAGE_NAME" || raise_error "IMAGE_NAME must be provided."
-# Construct a CONTEXT_FILE
-CONTEXT_FILE=/builds/worker/workspace/context.tar
+# The docker socket is mounted by the taskcluster worker in a way that prevents
+# us changing its permissions to allow the worker user to access it. Create a
+# proxy socket that the worker user can use.
+export DOCKER_SOCKET=/var/run/docker.proxy
+socat UNIX-LISTEN:$DOCKER_SOCKET,fork,group=worker,mode=0775 UNIX-CLIENT:/var/run/docker.sock </dev/null &
+trap "kill $!" EXIT
-# Run ./mach taskcluster-build-image with --context-only to build context
+# Build image
run-task \
--vcs-checkout "/builds/worker/checkouts/gecko" \
--sparse-profile build/sparse-profiles/docker-image \
-- \
/builds/worker/checkouts/gecko/mach taskcluster-build-image \
- --context-only "$CONTEXT_FILE" \
+ -t "$IMAGE_NAME:$HASH" \
"$IMAGE_NAME"
-test -f "$CONTEXT_FILE" || raise_error "Context file wasn't created"
# Create artifact folder (note that this must occur after run-task)
mkdir -p /builds/worker/workspace/artifacts
-# Post context tar-ball to docker daemon
-# This interacts directly with the docker remote API, see:
-# https://docs.docker.com/engine/reference/api/docker_remote_api_v1.18/
-curl -s --fail \
- -X POST \
- --header 'Content-Type: application/tar' \
- --data-binary "@$CONTEXT_FILE" \
- --unix-socket /var/run/docker.sock "http:/build?t=$IMAGE_NAME:$HASH" \
- | tee /tmp/docker-build.log \
- | jq -jr '(.status + .progress, .error | select(. != null) + "\n"), .stream | select(. != null)'
-
-# Exit non-zero if there is error entries in the log
-if result=$(jq -se 'add | .error' /tmp/docker-build.log); then
- raise_error "Image build failed: ${result}";
-fi
-
-# Sanity check that image was built successfully
-if ! tail -n 1 /tmp/docker-build.log | jq -r '.stream' | grep '^Successfully built' > /dev/null; then
- echo 'docker-build.log for debugging:';
- tail -n 50 /tmp/docker-build.log;
- raise_error "Image build log didn't with 'Successfully built'";
-fi
-
# Get image from docker daemon (try up to 10 times)
# This interacts directly with the docker remote API, see:
# https://docs.docker.com/engine/reference/api/docker_remote_api_v1.18/
#
# The script will retry up to 10 times.
# Disable quoting error until fixing the / escaping
# shellcheck disable=SC2086
/usr/local/bin/download-and-compress \
--- a/taskcluster/docker/image_builder/setup.sh
+++ b/taskcluster/docker/image_builder/setup.sh
@@ -3,19 +3,17 @@ set -v -e -x
export DEBIAN_FRONTEND=noninteractive
# Update apt-get lists
apt-get update -y
# Install dependencies
apt-get install -y --no-install-recommends \
- curl \
- tar \
- jq \
+ socat \
python \
python-requests \
python-requests-unixsocket
# Extra dependencies only needed for image building. Will be removed at
# end of script.
apt-get install -y python-pip
--- a/taskcluster/mach_commands.py
+++ b/taskcluster/mach_commands.py
@@ -416,25 +416,28 @@ class TaskClusterImagesProvider(MachComm
except Exception:
traceback.print_exc()
sys.exit(1)
@Command('taskcluster-build-image', category='ci',
description='Build a Docker image')
@CommandArgument('image_name',
help='Name of the image to build')
+ @CommandArgument('-t', '--tag',
+ help="tag that the image should be built as.",
+ metavar="name:tag")
@CommandArgument('--context-only',
help="File name the context tarball should be written to."
"with this option it will only build the context.tar.",
metavar='context.tar')
- def build_image(self, image_name, context_only):
+ def build_image(self, image_name, tag, context_only):
from taskgraph.docker import build_image, build_context
try:
if context_only is None:
- build_image(image_name, os.environ)
+ build_image(image_name, tag, os.environ)
else:
build_context(image_name, context_only, os.environ)
except Exception:
traceback.print_exc()
sys.exit(1)
@CommandProvider
--- a/taskcluster/taskgraph/docker.py
+++ b/taskcluster/taskgraph/docker.py
@@ -59,29 +59,29 @@ def build_context(name, outputFile, args
image_dir = docker.image_path(name)
if not os.path.isdir(image_dir):
raise Exception('image directory does not exist: %s' % image_dir)
docker.create_context_tar(GECKO, image_dir, outputFile, "", args)
-def build_image(name, args=None):
+def build_image(name, tag, args=None):
"""Build a Docker image of specified name.
Output from image building process will be printed to stdout.
"""
if not name:
raise ValueError('must provide a Docker image name')
image_dir = docker.image_path(name)
if not os.path.isdir(image_dir):
raise Exception('image directory does not exist: %s' % image_dir)
- tag = docker.docker_image(name, by_tag=True)
+ tag = tag or docker.docker_image(name, by_tag=True)
buf = BytesIO()
docker.stream_context_tar(GECKO, image_dir, buf, '', args)
docker.post_to_docker(buf.getvalue(), '/build', nocache=1, t=tag)
print('Successfully built %s and tagged with %s' % (name, tag))
if tag.endswith(':latest'):