#!/bin/bash
# This script holds library functions for setting up the Docker container build environment
# os::build::environment::create creates a docker container with the default variables.
# arguments are passed directly to the container, OS_BUILD_ENV_GOLANG, OS_BUILD_ENV_IMAGE,
# and OS_RELEASE_DOCKER_ARGS can be used to customize the container. The docker socket
# is mounted by default and the output of the command is the container id.
function os::build::environment::create() {
set -o errexit
local release_image="${OS_BUILD_ENV_IMAGE}"
local additional_context="${OS_BUILD_ENV_DOCKER_ARGS:-}"
if [[ "${OS_BUILD_ENV_USE_DOCKER:-y}" == "y" ]]; then
additional_context+="--privileged -v /var/run/docker.sock:/var/run/docker.sock"
if [[ "${OS_BUILD_ENV_LOCAL_DOCKER:-n}" == "y" ]]; then
# if OS_BUILD_ENV_LOCAL_DOCKER==y, add the local OS_ROOT as the bind mount to the working dir
# and set the running user to the current user
local workingdir
workingdir=$( os::build::environment::release::workingdir )
additional_context+=" -v ${OS_ROOT}:${workingdir} -u $(id -u)"
elif [[ -n "${OS_BUILD_ENV_REUSE_VOLUME:-}" ]]; then
# if OS_BUILD_ENV_REUSE_VOLUME is set, create a docker volume to store the working output so
# successive iterations can reuse shared code.
local workingdir
workingdir=$( os::build::environment::release::workingdir )
name="$( echo "${OS_BUILD_ENV_REUSE_VOLUME}" | tr '[:upper:]' '[:lower:]' )"
docker volume create --name "${name}" > /dev/null
additional_context+=" -v ${name}:${workingdir}"
fi
fi
local args
if [[ $# -eq 0 ]]; then
args=( "echo" "docker create ${additional_context} ${release_image}" )
else
args=( "$@" )
fi
# Create a new container to from the release environment
docker create ${additional_context} "${release_image}" "${args[@]}"
}
readonly -f os::build::environment::create
# os::build::environment::release::workingdir calculates the working directory for the current
# release image.
function os::build::environment::release::workingdir() {
set -o errexit
# get working directory
local container
container="$(docker create "${release_image}")"
local workingdir
workingdir="$(docker inspect -f '{{ index . "Config" "WorkingDir" }}' "${container}")"
docker rm "${container}" > /dev/null
echo "${workingdir}"
}
readonly -f os::build::environment::release::workingdir
# os::build::environment::cleanup stops and removes the container named in the argument
# (unless OS_BUILD_ENV_LEAVE_CONTAINER is set, in which case it will only stop the container).
function os::build::environment::cleanup() {
local container=$1
docker stop --time=0 "${container}" > /dev/null || true
if [[ -z "${OS_BUILD_ENV_LEAVE_CONTAINER:-}" ]]; then
docker rm "${container}" > /dev/null
fi
}
readonly -f os::build::environment::cleanup
# os::build::environment::start starts the container provided as the first argument
# using whatever content exists in the container already.
function os::build::environment::start() {
local container=$1
docker start "${container}" > /dev/null
docker logs -f "${container}"
local exitcode
exitcode="$( docker inspect --type container -f '{{ .State.ExitCode }}' "${container}" )"
# extract content from the image
if [[ -n "${OS_BUILD_ENV_PRESERVE-}" ]]; then
local workingdir
workingdir="$(docker inspect -f '{{ index . "Config" "WorkingDir" }}' "${container}")"
local oldIFS="${IFS}"
IFS=:
for path in ${OS_BUILD_ENV_PRESERVE}; do
local parent=.
if [[ "${path}" != "." ]]; then
parent="$( dirname "${path}" )"
mkdir -p "${parent}"
fi
docker cp "${container}:${workingdir}/${path}" "${parent}"
done
IFS="${oldIFS}"
fi
return "${exitcode}"
}
readonly -f os::build::environment::start
# os::build::environment::withsource starts the container provided as the first argument
# after copying in the contents of the current Git repository at HEAD (or, if specified,
# the ref specified in the second argument).
function os::build::environment::withsource() {
local container=$1
local commit=${2:-HEAD}
if [[ -n "${OS_BUILD_ENV_LOCAL_DOCKER-}" ]]; then
# running locally, no change necessary
os::build::get_version_vars
os::build::save_version_vars "${OS_ROOT}/os-version-defs"
else
local workingdir
workingdir="$(docker inspect -f '{{ index . "Config" "WorkingDir" }}' "${container}")"
if [[ -n "${OS_BUILD_ENV_REUSE_VOLUME-}" ]]; then
local excluded=()
local oldIFS="${IFS}"
IFS=:
for exclude in ${OS_BUILD_ENV_EXCLUDE:-_output}; do
excluded+=("--exclude=${exclude}")
done
IFS="${oldIFS}"
if which rsync &>/dev/null; then
local name
name="$( echo "${OS_BUILD_ENV_REUSE_VOLUME}" | tr '[:upper:]' '[:lower:]' )"
if ! rsync -a --blocking-io "${excluded[@]}" --omit-dir-times --numeric-ids -e "docker run --rm -i -v \"${name}:${workingdir}\" --entrypoint=/bin/bash \"${OS_BUILD_ENV_IMAGE}\" -c '\$@'" . remote:"${workingdir}"; then
# fall back to a tar if rsync is not in container
tar -cf - "${excluded[@]}" . | docker cp - "${container}:${workingdir}"
fi
else
tar -cf - "${excluded[@]}" . | docker cp - "${container}:${workingdir}"
fi
else
# Generate version definitions. Tree state is clean because we are pulling from git directly.
OS_GIT_TREE_STATE=clean os::build::get_version_vars
os::build::save_version_vars "/tmp/os-version-defs"
tar -cf - -C /tmp/ os-version-defs | docker cp - "${container}:${workingdir}"
git archive --format=tar "${commit}" | docker cp - "${container}:${workingdir}"
fi
fi
os::build::environment::start "${container}"
}
readonly -f os::build::environment::withsource
# os::build::environment::run launches the container with the provided arguments and
# the current commit (defaults to HEAD). The container is automatically cleaned up.
function os::build::environment::run() {
local commit="${OS_GIT_COMMIT:-HEAD}"
local volume="${OS_BUILD_ENV_REUSE_VOLUME:-}"
if [[ -z "${volume}" ]]; then
volume="origin-build-$( git rev-parse "${commit}" )"
fi
local container
container="$( OS_BUILD_ENV_REUSE_VOLUME=${volume} os::build::environment::create "$@" )"
trap "os::build::environment::cleanup ${container}" EXIT
os::build::environment::withsource "${container}" "${commit}"
}
readonly -f os::build::environment::run