mirror of
https://github.com/docker/compose.git
synced 2025-07-21 04:34:38 +02:00
Merge pull request #6641 from ulyssessouza/dockerfiles_refactor
Refactor Dockerfiles for generating musl binaries
This commit is contained in:
commit
8a89d94e15
85
Dockerfile
85
Dockerfile
@ -1,36 +1,71 @@
|
|||||||
FROM docker:18.06.1 as docker
|
ARG DOCKER_VERSION=18.09.5
|
||||||
FROM python:3.7.2-stretch
|
ARG PYTHON_VERSION=3.7.3
|
||||||
|
ARG BUILD_ALPINE_VERSION=3.9
|
||||||
|
ARG BUILD_DEBIAN_VERSION=slim-stretch
|
||||||
|
ARG RUNTIME_ALPINE_VERSION=3.9.3
|
||||||
|
ARG RUNTIME_DEBIAN_VERSION=stretch-20190326-slim
|
||||||
|
|
||||||
RUN set -ex; \
|
ARG BUILD_PLATFORM=alpine
|
||||||
apt-get update -qq; \
|
|
||||||
apt-get install -y \
|
|
||||||
locales \
|
|
||||||
python-dev \
|
|
||||||
git
|
|
||||||
|
|
||||||
COPY --from=docker /usr/local/bin/docker /usr/local/bin/docker
|
FROM docker:${DOCKER_VERSION} AS docker-cli
|
||||||
|
|
||||||
# Python3 requires a valid locale
|
FROM python:${PYTHON_VERSION}-alpine${BUILD_ALPINE_VERSION} AS build-alpine
|
||||||
RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen
|
RUN apk add --no-cache \
|
||||||
ENV LANG en_US.UTF-8
|
bash \
|
||||||
|
build-base \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
gcc \
|
||||||
|
git \
|
||||||
|
libc-dev \
|
||||||
|
libffi-dev \
|
||||||
|
libgcc \
|
||||||
|
make \
|
||||||
|
musl-dev \
|
||||||
|
openssl \
|
||||||
|
openssl-dev \
|
||||||
|
python2 \
|
||||||
|
python2-dev \
|
||||||
|
zlib-dev
|
||||||
|
ENV BUILD_BOOTLOADER=1
|
||||||
|
|
||||||
RUN useradd -d /home/user -m -s /bin/bash user
|
FROM python:${PYTHON_VERSION}-${BUILD_DEBIAN_VERSION} AS build-debian
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
gcc \
|
||||||
|
git \
|
||||||
|
libc-dev \
|
||||||
|
libgcc-6-dev \
|
||||||
|
make \
|
||||||
|
openssl \
|
||||||
|
python2.7-dev
|
||||||
|
|
||||||
|
FROM build-${BUILD_PLATFORM} AS build
|
||||||
|
COPY docker-compose-entrypoint.sh /usr/local/bin/
|
||||||
|
ENTRYPOINT ["sh", "/usr/local/bin/docker-compose-entrypoint.sh"]
|
||||||
|
COPY --from=docker-cli /usr/local/bin/docker /usr/local/bin/docker
|
||||||
WORKDIR /code/
|
WORKDIR /code/
|
||||||
|
|
||||||
# FIXME(chris-crone): virtualenv 16.3.0 breaks build, force 16.2.0 until fixed
|
# FIXME(chris-crone): virtualenv 16.3.0 breaks build, force 16.2.0 until fixed
|
||||||
RUN pip install virtualenv==16.2.0
|
RUN pip install virtualenv==16.2.0
|
||||||
RUN pip install tox==2.9.1
|
RUN pip install tox==2.9.1
|
||||||
|
|
||||||
ADD requirements.txt /code/
|
COPY requirements.txt .
|
||||||
ADD requirements-dev.txt /code/
|
COPY requirements-dev.txt .
|
||||||
ADD .pre-commit-config.yaml /code/
|
COPY .pre-commit-config.yaml .
|
||||||
ADD setup.py /code/
|
COPY tox.ini .
|
||||||
ADD tox.ini /code/
|
COPY setup.py .
|
||||||
ADD compose /code/compose/
|
COPY README.md .
|
||||||
ADD README.md /code/
|
COPY compose compose/
|
||||||
RUN tox --notest
|
RUN tox --notest
|
||||||
|
COPY . .
|
||||||
|
ARG GIT_COMMIT=unknown
|
||||||
|
ENV DOCKER_COMPOSE_GITSHA=$GIT_COMMIT
|
||||||
|
RUN script/build/linux-entrypoint
|
||||||
|
|
||||||
ADD . /code/
|
FROM alpine:${RUNTIME_ALPINE_VERSION} AS runtime-alpine
|
||||||
RUN chown -R user /code/
|
FROM debian:${RUNTIME_DEBIAN_VERSION} AS runtime-debian
|
||||||
|
FROM runtime-${BUILD_PLATFORM} AS runtime
|
||||||
ENTRYPOINT ["/code/.tox/py37/bin/docker-compose"]
|
COPY docker-compose-entrypoint.sh /usr/local/bin/
|
||||||
|
ENTRYPOINT ["sh", "/usr/local/bin/docker-compose-entrypoint.sh"]
|
||||||
|
COPY --from=docker-cli /usr/local/bin/docker /usr/local/bin/docker
|
||||||
|
COPY --from=build /usr/local/bin/docker-compose /usr/local/bin/docker-compose
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
FROM docker:18.06.1 as docker
|
|
||||||
FROM alpine:3.8
|
|
||||||
|
|
||||||
ENV GLIBC 2.28-r0
|
|
||||||
|
|
||||||
RUN apk update && apk add --no-cache openssl ca-certificates curl libgcc && \
|
|
||||||
curl -fsSL -o /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub && \
|
|
||||||
curl -fsSL -o glibc-$GLIBC.apk https://github.com/sgerrand/alpine-pkg-glibc/releases/download/$GLIBC/glibc-$GLIBC.apk && \
|
|
||||||
apk add --no-cache glibc-$GLIBC.apk && \
|
|
||||||
ln -s /lib/libz.so.1 /usr/glibc-compat/lib/ && \
|
|
||||||
ln -s /lib/libc.musl-x86_64.so.1 /usr/glibc-compat/lib && \
|
|
||||||
ln -s /usr/lib/libgcc_s.so.1 /usr/glibc-compat/lib && \
|
|
||||||
rm /etc/apk/keys/sgerrand.rsa.pub glibc-$GLIBC.apk && \
|
|
||||||
apk del curl
|
|
||||||
|
|
||||||
COPY --from=docker /usr/local/bin/docker /usr/local/bin/docker
|
|
||||||
COPY dist/docker-compose-Linux-x86_64 /usr/local/bin/docker-compose
|
|
||||||
|
|
||||||
ENTRYPOINT ["docker-compose"]
|
|
52
Jenkinsfile
vendored
52
Jenkinsfile
vendored
@ -1,29 +1,38 @@
|
|||||||
#!groovy
|
#!groovy
|
||||||
|
|
||||||
def image
|
def buildImage = { String baseImage ->
|
||||||
|
def image
|
||||||
def buildImage = { ->
|
|
||||||
wrappedNode(label: "ubuntu && !zfs", cleanWorkspace: true) {
|
wrappedNode(label: "ubuntu && !zfs", cleanWorkspace: true) {
|
||||||
stage("build image") {
|
stage("build image for \"${baseImage}\"") {
|
||||||
checkout(scm)
|
checkout(scm)
|
||||||
def imageName = "dockerbuildbot/compose:${gitCommit()}"
|
def imageName = "dockerbuildbot/compose:${baseImage}-${gitCommit()}"
|
||||||
image = docker.image(imageName)
|
image = docker.image(imageName)
|
||||||
try {
|
try {
|
||||||
image.pull()
|
image.pull()
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
image = docker.build(imageName, ".")
|
sh """GIT_COMMIT=\$(script/build/write-git-sha) && \\
|
||||||
image.push()
|
docker build -t ${imageName} \\
|
||||||
|
--target build \\
|
||||||
|
--build-arg BUILD_PLATFORM="${baseImage}" \\
|
||||||
|
--build-arg GIT_COMMIT="${GIT_COMMIT}" \\
|
||||||
|
.\\
|
||||||
|
"""
|
||||||
|
sh "docker push ${imageName}"
|
||||||
|
echo "${imageName}"
|
||||||
|
return imageName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
echo "image.id: ${image.id}"
|
||||||
|
return image.id
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_versions = { int number ->
|
def get_versions = { String imageId, int number ->
|
||||||
def docker_versions
|
def docker_versions
|
||||||
wrappedNode(label: "ubuntu && !zfs") {
|
wrappedNode(label: "ubuntu && !zfs") {
|
||||||
def result = sh(script: """docker run --rm \\
|
def result = sh(script: """docker run --rm \\
|
||||||
--entrypoint=/code/.tox/py27/bin/python \\
|
--entrypoint=/code/.tox/py27/bin/python \\
|
||||||
${image.id} \\
|
${imageId} \\
|
||||||
/code/script/test/versions.py -n ${number} docker/docker-ce recent
|
/code/script/test/versions.py -n ${number} docker/docker-ce recent
|
||||||
""", returnStdout: true
|
""", returnStdout: true
|
||||||
)
|
)
|
||||||
@ -35,6 +44,8 @@ def get_versions = { int number ->
|
|||||||
def runTests = { Map settings ->
|
def runTests = { Map settings ->
|
||||||
def dockerVersions = settings.get("dockerVersions", null)
|
def dockerVersions = settings.get("dockerVersions", null)
|
||||||
def pythonVersions = settings.get("pythonVersions", null)
|
def pythonVersions = settings.get("pythonVersions", null)
|
||||||
|
def baseImage = settings.get("baseImage", null)
|
||||||
|
def imageName = settings.get("image", null)
|
||||||
|
|
||||||
if (!pythonVersions) {
|
if (!pythonVersions) {
|
||||||
throw new Exception("Need Python versions to test. e.g.: `runTests(pythonVersions: 'py27,py37')`")
|
throw new Exception("Need Python versions to test. e.g.: `runTests(pythonVersions: 'py27,py37')`")
|
||||||
@ -45,7 +56,7 @@ def runTests = { Map settings ->
|
|||||||
|
|
||||||
{ ->
|
{ ->
|
||||||
wrappedNode(label: "ubuntu && !zfs", cleanWorkspace: true) {
|
wrappedNode(label: "ubuntu && !zfs", cleanWorkspace: true) {
|
||||||
stage("test python=${pythonVersions} / docker=${dockerVersions}") {
|
stage("test python=${pythonVersions} / docker=${dockerVersions} / baseImage=${baseImage}") {
|
||||||
checkout(scm)
|
checkout(scm)
|
||||||
def storageDriver = sh(script: 'docker info | awk -F \': \' \'$1 == "Storage Driver" { print $2; exit }\'', returnStdout: true).trim()
|
def storageDriver = sh(script: 'docker info | awk -F \': \' \'$1 == "Storage Driver" { print $2; exit }\'', returnStdout: true).trim()
|
||||||
echo "Using local system's storage driver: ${storageDriver}"
|
echo "Using local system's storage driver: ${storageDriver}"
|
||||||
@ -55,13 +66,13 @@ def runTests = { Map settings ->
|
|||||||
--privileged \\
|
--privileged \\
|
||||||
--volume="\$(pwd)/.git:/code/.git" \\
|
--volume="\$(pwd)/.git:/code/.git" \\
|
||||||
--volume="/var/run/docker.sock:/var/run/docker.sock" \\
|
--volume="/var/run/docker.sock:/var/run/docker.sock" \\
|
||||||
-e "TAG=${image.id}" \\
|
-e "TAG=${imageName}" \\
|
||||||
-e "STORAGE_DRIVER=${storageDriver}" \\
|
-e "STORAGE_DRIVER=${storageDriver}" \\
|
||||||
-e "DOCKER_VERSIONS=${dockerVersions}" \\
|
-e "DOCKER_VERSIONS=${dockerVersions}" \\
|
||||||
-e "BUILD_NUMBER=\$BUILD_TAG" \\
|
-e "BUILD_NUMBER=\$BUILD_TAG" \\
|
||||||
-e "PY_TEST_VERSIONS=${pythonVersions}" \\
|
-e "PY_TEST_VERSIONS=${pythonVersions}" \\
|
||||||
--entrypoint="script/test/ci" \\
|
--entrypoint="script/test/ci" \\
|
||||||
${image.id} \\
|
${imageName} \\
|
||||||
--verbose
|
--verbose
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
@ -69,15 +80,16 @@ def runTests = { Map settings ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildImage()
|
|
||||||
|
|
||||||
def testMatrix = [failFast: true]
|
def testMatrix = [failFast: true]
|
||||||
def docker_versions = get_versions(2)
|
def baseImages = ['alpine', 'debian']
|
||||||
|
def pythonVersions = ['py27', 'py37']
|
||||||
for (int i = 0; i < docker_versions.length; i++) {
|
baseImages.each { baseImage ->
|
||||||
def dockerVersion = docker_versions[i]
|
def imageName = buildImage(baseImage)
|
||||||
testMatrix["${dockerVersion}_py27"] = runTests([dockerVersions: dockerVersion, pythonVersions: "py27"])
|
get_versions(imageName, 2).each { dockerVersion ->
|
||||||
testMatrix["${dockerVersion}_py37"] = runTests([dockerVersions: dockerVersion, pythonVersions: "py37"])
|
pythonVersions.each { pyVersion ->
|
||||||
|
testMatrix["${baseImage}_${dockerVersion}_${pyVersion}"] = runTests([baseImage: baseImage, image: imageName, dockerVersions: dockerVersion, pythonVersions: pyVersion])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parallel(testMatrix)
|
parallel(testMatrix)
|
||||||
|
20
docker-compose-entrypoint.sh
Executable file
20
docker-compose-entrypoint.sh
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# first arg is `-f` or `--some-option`
|
||||||
|
if [ "${1#-}" != "$1" ]; then
|
||||||
|
set -- docker-compose "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if our command is a valid Docker subcommand, let's invoke it through Docker instead
|
||||||
|
# (this allows for "docker run docker ps", etc)
|
||||||
|
if docker-compose help "$1" > /dev/null 2>&1; then
|
||||||
|
set -- docker-compose "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if we have "--link some-docker:docker" and not DOCKER_HOST, let's set DOCKER_HOST automatically
|
||||||
|
if [ -z "$DOCKER_HOST" -a "$DOCKER_PORT_2375_TCP" ]; then
|
||||||
|
export DOCKER_HOST='tcp://docker:2375'
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$@"
|
13
pyinstaller/ldd
Executable file
13
pyinstaller/ldd
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# From http://wiki.musl-libc.org/wiki/FAQ#Q:_where_is_ldd_.3F
|
||||||
|
#
|
||||||
|
# Musl's dynlinker comes with ldd functionality built in. just create a
|
||||||
|
# symlink from ld-musl-$ARCH.so to /bin/ldd. If the dynlinker was started
|
||||||
|
# as "ldd", it will detect that and print the appropriate DSO information.
|
||||||
|
#
|
||||||
|
# Instead, this string replaced "ldd" with the package so that pyinstaller
|
||||||
|
# can find the actual lib.
|
||||||
|
exec /usr/bin/ldd "$@" | \
|
||||||
|
sed -r 's/([^[:space:]]+) => ldd/\1 => \/lib\/\1/g' | \
|
||||||
|
sed -r 's/ldd \(.*\)//g'
|
@ -7,11 +7,14 @@ if [ -z "$1" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TAG=$1
|
TAG="$1"
|
||||||
|
|
||||||
VERSION="$(python setup.py --version)"
|
VERSION="$(python setup.py --version)"
|
||||||
|
|
||||||
./script/build/write-git-sha
|
DOCKER_COMPOSE_GITSHA="$(script/build/write-git-sha)"
|
||||||
|
echo "${DOCKER_COMPOSE_GITSHA}" > compose/GITSHA
|
||||||
python setup.py sdist bdist_wheel
|
python setup.py sdist bdist_wheel
|
||||||
./script/build/linux
|
|
||||||
docker build -t docker/compose:$TAG -f Dockerfile.run .
|
docker build \
|
||||||
|
--build-arg GIT_COMMIT="${DOCKER_COMPOSE_GITSHA}" \
|
||||||
|
-t "${TAG}" .
|
||||||
|
@ -4,10 +4,14 @@ set -ex
|
|||||||
|
|
||||||
./script/clean
|
./script/clean
|
||||||
|
|
||||||
TAG="docker-compose"
|
DOCKER_COMPOSE_GITSHA="$(script/build/write-git-sha)"
|
||||||
docker build -t "$TAG" .
|
TAG="docker/compose:tmp-glibc-linux-binary-${DOCKER_COMPOSE_GITSHA}"
|
||||||
docker run \
|
|
||||||
--rm --entrypoint="script/build/linux-entrypoint" \
|
docker build -t "${TAG}" . \
|
||||||
-v $(pwd)/dist:/code/dist \
|
--build-arg BUILD_PLATFORM=debian \
|
||||||
-v $(pwd)/.git:/code/.git \
|
--build-arg GIT_COMMIT="${DOCKER_COMPOSE_GITSHA}"
|
||||||
"$TAG"
|
TMP_CONTAINER=$(docker create "${TAG}")
|
||||||
|
mkdir -p dist
|
||||||
|
docker cp "${TMP_CONTAINER}":/usr/local/bin/docker-compose dist/docker-compose-Linux-x86_64
|
||||||
|
docker container rm -f "${TMP_CONTAINER}"
|
||||||
|
docker image rm -f "${TAG}"
|
||||||
|
@ -2,14 +2,38 @@
|
|||||||
|
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
TARGET=dist/docker-compose-$(uname -s)-$(uname -m)
|
CODE_PATH=/code
|
||||||
VENV=/code/.tox/py37
|
VENV="${CODE_PATH}"/.tox/py37
|
||||||
|
|
||||||
mkdir -p `pwd`/dist
|
cd "${CODE_PATH}"
|
||||||
chmod 777 `pwd`/dist
|
mkdir -p dist
|
||||||
|
chmod 777 dist
|
||||||
|
|
||||||
$VENV/bin/pip install -q -r requirements-build.txt
|
"${VENV}"/bin/pip3 install -q -r requirements-build.txt
|
||||||
./script/build/write-git-sha
|
|
||||||
su -c "$VENV/bin/pyinstaller docker-compose.spec" user
|
# TODO(ulyssessouza) To check if really needed
|
||||||
mv dist/docker-compose $TARGET
|
if [ -z "${DOCKER_COMPOSE_GITSHA}" ]; then
|
||||||
$TARGET version
|
DOCKER_COMPOSE_GITSHA="$(script/build/write-git-sha)"
|
||||||
|
fi
|
||||||
|
echo "${DOCKER_COMPOSE_GITSHA}" > compose/GITSHA
|
||||||
|
|
||||||
|
export PATH="${CODE_PATH}/pyinstaller:${PATH}"
|
||||||
|
|
||||||
|
if [ ! -z "${BUILD_BOOTLOADER}" ]; then
|
||||||
|
# Build bootloader for alpine
|
||||||
|
git clone --single-branch --branch master https://github.com/pyinstaller/pyinstaller.git /tmp/pyinstaller
|
||||||
|
cd /tmp/pyinstaller/bootloader
|
||||||
|
git checkout v3.4
|
||||||
|
"${VENV}"/bin/python3 ./waf configure --no-lsb all
|
||||||
|
"${VENV}"/bin/pip3 install ..
|
||||||
|
cd "${CODE_PATH}"
|
||||||
|
rm -Rf /tmp/pyinstaller
|
||||||
|
else
|
||||||
|
echo "NOT compiling bootloader!!!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${VENV}"/bin/pyinstaller --exclude-module pycrypto --exclude-module PyInstaller docker-compose.spec
|
||||||
|
ls -la dist/
|
||||||
|
ldd dist/docker-compose
|
||||||
|
mv dist/docker-compose /usr/local/bin
|
||||||
|
docker-compose version
|
||||||
|
@ -5,11 +5,12 @@ TOOLCHAIN_PATH="$(realpath $(dirname $0)/../../build/toolchain)"
|
|||||||
|
|
||||||
rm -rf venv
|
rm -rf venv
|
||||||
|
|
||||||
virtualenv -p ${TOOLCHAIN_PATH}/bin/python3 venv
|
virtualenv -p "${TOOLCHAIN_PATH}"/bin/python3 venv
|
||||||
venv/bin/pip install -r requirements.txt
|
venv/bin/pip install -r requirements.txt
|
||||||
venv/bin/pip install -r requirements-build.txt
|
venv/bin/pip install -r requirements-build.txt
|
||||||
venv/bin/pip install --no-deps .
|
venv/bin/pip install --no-deps .
|
||||||
./script/build/write-git-sha
|
DOCKER_COMPOSE_GITSHA="$(script/build/write-git-sha)"
|
||||||
|
echo "${DOCKER_COMPOSE_GITSHA}" > compose/GITSHA
|
||||||
venv/bin/pyinstaller docker-compose.spec
|
venv/bin/pyinstaller docker-compose.spec
|
||||||
mv dist/docker-compose dist/docker-compose-Darwin-x86_64
|
mv dist/docker-compose dist/docker-compose-Darwin-x86_64
|
||||||
dist/docker-compose-Darwin-x86_64 version
|
dist/docker-compose-Darwin-x86_64 version
|
||||||
|
@ -7,11 +7,12 @@ if [ -z "$1" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TAG=$1
|
TAG="$1"
|
||||||
|
IMAGE="docker/compose-tests"
|
||||||
|
|
||||||
docker build -t docker-compose-tests:tmp .
|
DOCKER_COMPOSE_GITSHA="$(script/build/write-git-sha)"
|
||||||
ctnr_id=$(docker create --entrypoint=tox docker-compose-tests:tmp)
|
docker build -t "${IMAGE}:${TAG}" . \
|
||||||
docker commit $ctnr_id docker/compose-tests:latest
|
--target build \
|
||||||
docker tag docker/compose-tests:latest docker/compose-tests:$TAG
|
--build-arg BUILD_PLATFORM="debian" \
|
||||||
docker rm -f $ctnr_id
|
--build-arg GIT_COMMIT="${DOCKER_COMPOSE_GITSHA}"
|
||||||
docker rmi -f docker-compose-tests:tmp
|
docker tag "${IMAGE}":"${TAG}" "${IMAGE}":latest
|
||||||
|
@ -9,4 +9,4 @@ if [[ "${?}" != "0" ]]; then
|
|||||||
echo "Couldn't get revision of the git repository. Setting to 'unknown' instead"
|
echo "Couldn't get revision of the git repository. Setting to 'unknown' instead"
|
||||||
DOCKER_COMPOSE_GITSHA="unknown"
|
DOCKER_COMPOSE_GITSHA="unknown"
|
||||||
fi
|
fi
|
||||||
echo "${DOCKER_COMPOSE_GITSHA}" > compose/GITSHA
|
echo "${DOCKER_COMPOSE_GITSHA}"
|
||||||
|
@ -204,7 +204,7 @@ def resume(args):
|
|||||||
delete_assets(gh_release)
|
delete_assets(gh_release)
|
||||||
upload_assets(gh_release, files)
|
upload_assets(gh_release, files)
|
||||||
img_manager = ImageManager(args.release)
|
img_manager = ImageManager(args.release)
|
||||||
img_manager.build_images(repository, files)
|
img_manager.build_images(repository)
|
||||||
except ScriptError as e:
|
except ScriptError as e:
|
||||||
print(e)
|
print(e)
|
||||||
return 1
|
return 1
|
||||||
@ -244,7 +244,7 @@ def start(args):
|
|||||||
gh_release = create_release_draft(repository, args.release, pr_data, files)
|
gh_release = create_release_draft(repository, args.release, pr_data, files)
|
||||||
upload_assets(gh_release, files)
|
upload_assets(gh_release, files)
|
||||||
img_manager = ImageManager(args.release)
|
img_manager = ImageManager(args.release)
|
||||||
img_manager.build_images(repository, files)
|
img_manager.build_images(repository)
|
||||||
except ScriptError as e:
|
except ScriptError as e:
|
||||||
print(e)
|
print(e)
|
||||||
return 1
|
return 1
|
||||||
@ -258,7 +258,8 @@ def finalize(args):
|
|||||||
try:
|
try:
|
||||||
check_pypirc()
|
check_pypirc()
|
||||||
repository = Repository(REPO_ROOT, args.repo)
|
repository = Repository(REPO_ROOT, args.repo)
|
||||||
img_manager = ImageManager(args.release)
|
tag_as_latest = _check_if_tag_latest(args.release)
|
||||||
|
img_manager = ImageManager(args.release, tag_as_latest)
|
||||||
pr_data = repository.find_release_pr(args.release)
|
pr_data = repository.find_release_pr(args.release)
|
||||||
if not pr_data:
|
if not pr_data:
|
||||||
raise ScriptError('No PR found for {}'.format(args.release))
|
raise ScriptError('No PR found for {}'.format(args.release))
|
||||||
@ -314,6 +315,12 @@ EPILOG = '''Example uses:
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
# Checks if this version respects the GA version format ('x.y.z') and not an RC
|
||||||
|
def _check_if_tag_latest(version):
|
||||||
|
ga_version = all(n.isdigit() for n in version.split('.')) and version.count('.') == 2
|
||||||
|
return ga_version and yesno('Should this release be tagged as \"latest\"? Y/n', default=True)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if 'GITHUB_TOKEN' not in os.environ:
|
if 'GITHUB_TOKEN' not in os.environ:
|
||||||
print('GITHUB_TOKEN environment variable must be set')
|
print('GITHUB_TOKEN environment variable must be set')
|
||||||
|
@ -5,18 +5,29 @@ from __future__ import unicode_literals
|
|||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import shutil
|
|
||||||
|
|
||||||
import docker
|
import docker
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from .const import NAME
|
||||||
from .const import REPO_ROOT
|
from .const import REPO_ROOT
|
||||||
from .utils import ScriptError
|
from .utils import ScriptError
|
||||||
|
|
||||||
|
|
||||||
|
class Platform(Enum):
|
||||||
|
ALPINE = 'alpine'
|
||||||
|
DEBIAN = 'debian'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
class ImageManager(object):
|
class ImageManager(object):
|
||||||
def __init__(self, version):
|
def __init__(self, version, latest=False):
|
||||||
|
self.built_tags = []
|
||||||
self.docker_client = docker.APIClient(**docker.utils.kwargs_from_env())
|
self.docker_client = docker.APIClient(**docker.utils.kwargs_from_env())
|
||||||
self.version = version
|
self.version = version
|
||||||
|
self.latest = latest
|
||||||
if 'HUB_CREDENTIALS' in os.environ:
|
if 'HUB_CREDENTIALS' in os.environ:
|
||||||
print('HUB_CREDENTIALS found in environment, issuing login')
|
print('HUB_CREDENTIALS found in environment, issuing login')
|
||||||
credentials = json.loads(base64.urlsafe_b64decode(os.environ['HUB_CREDENTIALS']))
|
credentials = json.loads(base64.urlsafe_b64decode(os.environ['HUB_CREDENTIALS']))
|
||||||
@ -24,16 +35,31 @@ class ImageManager(object):
|
|||||||
username=credentials['Username'], password=credentials['Password']
|
username=credentials['Username'], password=credentials['Password']
|
||||||
)
|
)
|
||||||
|
|
||||||
def build_images(self, repository, files):
|
def _tag(self, image, existing_tag, new_tag):
|
||||||
print("Building release images...")
|
existing_repo_tag = '{image}:{tag}'.format(image=image, tag=existing_tag)
|
||||||
repository.write_git_sha()
|
new_repo_tag = '{image}:{tag}'.format(image=image, tag=new_tag)
|
||||||
distdir = os.path.join(REPO_ROOT, 'dist')
|
self.docker_client.tag(existing_repo_tag, new_repo_tag)
|
||||||
os.makedirs(distdir, exist_ok=True)
|
self.built_tags.append(new_repo_tag)
|
||||||
shutil.copy(files['docker-compose-Linux-x86_64'][0], distdir)
|
|
||||||
os.chmod(os.path.join(distdir, 'docker-compose-Linux-x86_64'), 0o755)
|
def build_runtime_image(self, repository, platform):
|
||||||
print('Building docker/compose image')
|
git_sha = repository.write_git_sha()
|
||||||
|
compose_image_base_name = NAME
|
||||||
|
print('Building {image} image ({platform} based)'.format(
|
||||||
|
image=compose_image_base_name,
|
||||||
|
platform=platform
|
||||||
|
))
|
||||||
|
full_version = '{version}-{platform}'.format(version=self.version, platform=platform)
|
||||||
|
build_tag = '{image_base_image}:{full_version}'.format(
|
||||||
|
image_base_image=compose_image_base_name,
|
||||||
|
full_version=full_version
|
||||||
|
)
|
||||||
logstream = self.docker_client.build(
|
logstream = self.docker_client.build(
|
||||||
REPO_ROOT, tag='docker/compose:{}'.format(self.version), dockerfile='Dockerfile.run',
|
REPO_ROOT,
|
||||||
|
tag=build_tag,
|
||||||
|
buildargs={
|
||||||
|
'BUILD_PLATFORM': platform.value,
|
||||||
|
'GIT_COMMIT': git_sha,
|
||||||
|
},
|
||||||
decode=True
|
decode=True
|
||||||
)
|
)
|
||||||
for chunk in logstream:
|
for chunk in logstream:
|
||||||
@ -42,9 +68,32 @@ class ImageManager(object):
|
|||||||
if 'stream' in chunk:
|
if 'stream' in chunk:
|
||||||
print(chunk['stream'], end='')
|
print(chunk['stream'], end='')
|
||||||
|
|
||||||
print('Building test image (for UCP e2e)')
|
self.built_tags.append(build_tag)
|
||||||
|
if platform == Platform.ALPINE:
|
||||||
|
self._tag(compose_image_base_name, full_version, self.version)
|
||||||
|
if self.latest:
|
||||||
|
self._tag(compose_image_base_name, full_version, platform)
|
||||||
|
if platform == Platform.ALPINE:
|
||||||
|
self._tag(compose_image_base_name, full_version, 'latest')
|
||||||
|
|
||||||
|
# Used for producing a test image for UCP
|
||||||
|
def build_ucp_test_image(self, repository):
|
||||||
|
print('Building test image (debian based for UCP e2e)')
|
||||||
|
git_sha = repository.write_git_sha()
|
||||||
|
compose_tests_image_base_name = NAME + '-tests'
|
||||||
|
ucp_test_image_tag = '{image}:{tag}'.format(
|
||||||
|
image=compose_tests_image_base_name,
|
||||||
|
tag=self.version
|
||||||
|
)
|
||||||
logstream = self.docker_client.build(
|
logstream = self.docker_client.build(
|
||||||
REPO_ROOT, tag='docker-compose-tests:tmp', decode=True
|
REPO_ROOT,
|
||||||
|
tag=ucp_test_image_tag,
|
||||||
|
target='build',
|
||||||
|
buildargs={
|
||||||
|
'BUILD_PLATFORM': Platform.DEBIAN.value,
|
||||||
|
'GIT_COMMIT': git_sha,
|
||||||
|
},
|
||||||
|
decode=True
|
||||||
)
|
)
|
||||||
for chunk in logstream:
|
for chunk in logstream:
|
||||||
if 'error' in chunk:
|
if 'error' in chunk:
|
||||||
@ -52,26 +101,16 @@ class ImageManager(object):
|
|||||||
if 'stream' in chunk:
|
if 'stream' in chunk:
|
||||||
print(chunk['stream'], end='')
|
print(chunk['stream'], end='')
|
||||||
|
|
||||||
container = self.docker_client.create_container(
|
self.built_tags.append(ucp_test_image_tag)
|
||||||
'docker-compose-tests:tmp', entrypoint='tox'
|
self._tag(compose_tests_image_base_name, self.version, 'latest')
|
||||||
)
|
|
||||||
self.docker_client.commit(container, 'docker/compose-tests', 'latest')
|
|
||||||
self.docker_client.tag(
|
|
||||||
'docker/compose-tests:latest', 'docker/compose-tests:{}'.format(self.version)
|
|
||||||
)
|
|
||||||
self.docker_client.remove_container(container, force=True)
|
|
||||||
self.docker_client.remove_image('docker-compose-tests:tmp', force=True)
|
|
||||||
|
|
||||||
@property
|
def build_images(self, repository):
|
||||||
def image_names(self):
|
self.build_runtime_image(repository, Platform.ALPINE)
|
||||||
return [
|
self.build_runtime_image(repository, Platform.DEBIAN)
|
||||||
'docker/compose-tests:latest',
|
self.build_ucp_test_image(repository)
|
||||||
'docker/compose-tests:{}'.format(self.version),
|
|
||||||
'docker/compose:{}'.format(self.version)
|
|
||||||
]
|
|
||||||
|
|
||||||
def check_images(self):
|
def check_images(self):
|
||||||
for name in self.image_names:
|
for name in self.built_tags:
|
||||||
try:
|
try:
|
||||||
self.docker_client.inspect_image(name)
|
self.docker_client.inspect_image(name)
|
||||||
except docker.errors.ImageNotFound:
|
except docker.errors.ImageNotFound:
|
||||||
@ -80,7 +119,7 @@ class ImageManager(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def push_images(self):
|
def push_images(self):
|
||||||
for name in self.image_names:
|
for name in self.built_tags:
|
||||||
print('Pushing {} to Docker Hub'.format(name))
|
print('Pushing {} to Docker Hub'.format(name))
|
||||||
logstream = self.docker_client.push(name, stream=True, decode=True)
|
logstream = self.docker_client.push(name, stream=True, decode=True)
|
||||||
for chunk in logstream:
|
for chunk in logstream:
|
||||||
|
@ -175,6 +175,7 @@ class Repository(object):
|
|||||||
def write_git_sha(self):
|
def write_git_sha(self):
|
||||||
with open(os.path.join(REPO_ROOT, 'compose', 'GITSHA'), 'w') as f:
|
with open(os.path.join(REPO_ROOT, 'compose', 'GITSHA'), 'w') as f:
|
||||||
f.write(self.git_repo.head.commit.hexsha[:7])
|
f.write(self.git_repo.head.commit.hexsha[:7])
|
||||||
|
return self.git_repo.head.commit.hexsha[:7]
|
||||||
|
|
||||||
def cherry_pick_prs(self, release_branch, ids):
|
def cherry_pick_prs(self, release_branch, ids):
|
||||||
if not ids:
|
if not ids:
|
||||||
|
@ -48,7 +48,7 @@ fi
|
|||||||
|
|
||||||
# Only allocate tty if we detect one
|
# Only allocate tty if we detect one
|
||||||
if [ -t 0 -a -t 1 ]; then
|
if [ -t 0 -a -t 1 ]; then
|
||||||
DOCKER_RUN_OPTIONS="$DOCKER_RUN_OPTIONS -t"
|
DOCKER_RUN_OPTIONS="$DOCKER_RUN_OPTIONS -t"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Always set -i to support piped and terminal input in run/exec
|
# Always set -i to support piped and terminal input in run/exec
|
||||||
|
@ -8,8 +8,7 @@ set -e
|
|||||||
docker run --rm \
|
docker run --rm \
|
||||||
--tty \
|
--tty \
|
||||||
${GIT_VOLUME} \
|
${GIT_VOLUME} \
|
||||||
--entrypoint="tox" \
|
"$TAG" tox -e pre-commit
|
||||||
"$TAG" -e pre-commit
|
|
||||||
|
|
||||||
get_versions="docker run --rm
|
get_versions="docker run --rm
|
||||||
--entrypoint=/code/.tox/py27/bin/python
|
--entrypoint=/code/.tox/py27/bin/python
|
||||||
|
@ -20,6 +20,3 @@ export DOCKER_DAEMON_ARGS="--storage-driver=$STORAGE_DRIVER"
|
|||||||
|
|
||||||
GIT_VOLUME="--volumes-from=$(hostname)"
|
GIT_VOLUME="--volumes-from=$(hostname)"
|
||||||
. script/test/all
|
. script/test/all
|
||||||
|
|
||||||
>&2 echo "Building Linux binary"
|
|
||||||
. script/build/linux-entrypoint
|
|
||||||
|
@ -3,17 +3,18 @@
|
|||||||
|
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
TAG="docker-compose:$(git rev-parse --short HEAD)"
|
TAG="docker-compose:alpine-$(git rev-parse --short HEAD)"
|
||||||
|
|
||||||
# By default use the Dockerfile, but can be overridden to use an alternative file
|
# By default use the Dockerfile, but can be overridden to use an alternative file
|
||||||
# e.g DOCKERFILE=Dockerfile.armhf script/test/default
|
# e.g DOCKERFILE=Dockerfile.armhf script/test/default
|
||||||
DOCKERFILE="${DOCKERFILE:-Dockerfile}"
|
DOCKERFILE="${DOCKERFILE:-Dockerfile}"
|
||||||
|
DOCKER_BUILD_TARGET="${DOCKER_BUILD_TARGET:-build}"
|
||||||
|
|
||||||
rm -rf coverage-html
|
rm -rf coverage-html
|
||||||
# Create the host directory so it's owned by $USER
|
# Create the host directory so it's owned by $USER
|
||||||
mkdir -p coverage-html
|
mkdir -p coverage-html
|
||||||
|
|
||||||
docker build -f ${DOCKERFILE} -t "$TAG" .
|
docker build -f "${DOCKERFILE}" -t "${TAG}" --target "${DOCKER_BUILD_TARGET}" .
|
||||||
|
|
||||||
GIT_VOLUME="--volume=$(pwd)/.git:/code/.git"
|
GIT_VOLUME="--volume=$(pwd)/.git:/code/.git"
|
||||||
. script/test/all
|
. script/test/all
|
||||||
|
@ -2347,6 +2347,7 @@ class CLITestCase(DockerClientTestCase):
|
|||||||
assert 'another' in result.stdout
|
assert 'another' in result.stdout
|
||||||
assert 'exited with code 0' in result.stdout
|
assert 'exited with code 0' in result.stdout
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="race condition between up and logs")
|
||||||
def test_logs_follow_logs_from_new_containers(self):
|
def test_logs_follow_logs_from_new_containers(self):
|
||||||
self.base_dir = 'tests/fixtures/logs-composefile'
|
self.base_dir = 'tests/fixtures/logs-composefile'
|
||||||
self.dispatch(['up', '-d', 'simple'])
|
self.dispatch(['up', '-d', 'simple'])
|
||||||
@ -2393,6 +2394,7 @@ class CLITestCase(DockerClientTestCase):
|
|||||||
) == 3
|
) == 3
|
||||||
assert result.stdout.count('world') == 3
|
assert result.stdout.count('world') == 3
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="race condition between up and logs")
|
||||||
def test_logs_default(self):
|
def test_logs_default(self):
|
||||||
self.base_dir = 'tests/fixtures/logs-composefile'
|
self.base_dir = 'tests/fixtures/logs-composefile'
|
||||||
self.dispatch(['up', '-d'])
|
self.dispatch(['up', '-d'])
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
simple:
|
simple:
|
||||||
image: busybox:latest
|
image: busybox:latest
|
||||||
command: sh -c "echo hello && tail -f /dev/null"
|
command: sh -c "sleep 1 && echo hello && tail -f /dev/null"
|
||||||
another:
|
another:
|
||||||
image: busybox:latest
|
image: busybox:latest
|
||||||
command: sh -c "echo test"
|
command: sh -c "sleep 1 && echo test"
|
||||||
|
@ -3,5 +3,5 @@ simple:
|
|||||||
command: sh -c "echo hello && tail -f /dev/null"
|
command: sh -c "echo hello && tail -f /dev/null"
|
||||||
another:
|
another:
|
||||||
image: busybox:latest
|
image: busybox:latest
|
||||||
command: sh -c "sleep 0.5 && echo world && /bin/false"
|
command: sh -c "sleep 2 && echo world && /bin/false"
|
||||||
restart: "on-failure:2"
|
restart: "on-failure:2"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user