blob: 70bea97c77df5ea3b5f27a6b796c314960c169b4 [file] [log] [blame]
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
# This shell script makes it easier to build multi-platform
# architecture Docker containers on an x86_64 host.
#
# For more reading:
# https://github.com/multiarch/qemu-user-static
# https://lobradov.github.io/Building-docker-multiarch-images/
# https://github.com/jessfraz/irssi/blob/master/.travis.yml
# https://engineering.docker.com/2019/04/multi-arch-images/
# https://github.com/docker/buildx
set -e
PROMPT="Are you sure (y/n)? "
QEMU="YES"
PLATFORMS="amd64 arm64v8 ppc64le"
BUILDX_PLATFORMS="linux/amd64,linux/arm64/v8,linux/ppc64le"
prompt() {
if [ -z "${PROMPT}" ]
then
return
fi
if [ "$1" ]
then
echo "$1"
fi
read -p "${PROMPT}"
if [[ $REPLY =~ ^[Yy]$ ]]
then
return
else
exit 0
fi
}
update_qemu() {
# necessary locally after every reboot, not sure why....update related maybe?
# basically harmless to run everytime, except for elevated privs necessary.
# disable with -n flag
docker rmi multiarch/qemu-user-static >/dev/null 2>&1 || true
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker rmi multiarch/qemu-user-static
}
clean() {
echo $#
if [ $# -eq 0 ]
then
regex="*"
ADD_PROMPT="This will remove *ALL* local apache/couchdb Docker images!"
elif [ $# -eq 1 ]
then
regex=$1
ADD_PROMPT="This will remove *ALL* apache/couchdb images matching regex '${1}' !"
else
usage
fi
prompt "${ADD_PROMPT}"
docker images --filter=reference="apache/couchdb:${regex}" | tr -s ' ' | cut -d ' ' -f 2 | while read tag
do
if [ ${tag} ] && [ ${tag} = "TAG" ]
then
continue
fi
docker rmi apache/couchdb:$tag
done
}
# Builds a specific version
build() {
VERSION=$1
ARCH=${2:-amd64}
FROMIMG="$(awk '$1 == toupper("FROM") { print $2 }' $VERSION/Dockerfile)"
CURRARCH=$(docker run --rm -t ${FROMIMG} uname -m)
if [ ${CURRARCH} != ${ARCH} ]
then
docker rmi ${FROMIMG}
docker pull "${ARCH}/${FROMIMG}"
docker tag "${ARCH}/${FROMIMG}" "${FROMIMG}"
fi
docker build -t apache/couchdb:${ARCH}-${VERSION} ${VERSION}
echo "CouchDB ${VERSION} for ${ARCH} built as apache/couchdb:${ARCH}-${VERSION}."
}
# Builds all platforms for a specific version, local only
# We can't do this with docker buildx, see https://github.com/docker/buildx/issues/166#issuecomment-562729523
build-all() {
VERSION=$1
for ARCH in ${PLATFORMS}; do
echo "Starting ${ARCH} at $(date)..."
build $1 ${ARCH}
echo ""
done
}
# Push locally built versions using above technique
push() {
if [ $2 ]
then
tag_as=$2
else
tag_as=$1
fi
docker manifest create apache/couchdb:$tag_as \
apache/couchdb:amd64-$1 \
apache/couchdb:arm64v8-$1 \
apache/couchdb:ppc64le-$1
docker manifest annotate apache/couchdb:$tag_as \
apache/couchdb:arm64v8-$1 --os linux --arch arm64 --variant v8
docker manifest annotate apache/couchdb:$tag_as \
apache/couchdb:ppc64le-$1 --os linux --arch ppc64le
docker manifest push --purge apache/couchdb:$tag_as
docker manifest inspect apache/couchdb:$tag_as
}
# Builds all platforms for a specific version and pushes to the registry
buildx() {
if [ $2 ]
then
tag_as=$2
else
tag_as=$1
fi
docker buildx rm apache-couchdb >/dev/null 2>&1 || true
docker buildx create --name apache-couchdb
docker buildx use apache-couchdb
docker buildx inspect --bootstrap
echo "Starting buildx build at $(date)..."
docker buildx build --platform ${BUILDX_PLATFORMS} --tag apache/couchdb:$tag_as --push $1
echo ""
}
usage() {
cat << EOF
$0 <command> <-f> <-n> [OPTIONS]
Options:
-f Skip confirmation prompt.
-n Do not install QEMU and binfmt_misc
(build commands only)
General commands:
clean Removes ALL local apache/couchdb images (!!)
clean <regex> Removes ALL local images with matching tags.
\`docker build\` commands:
version #.#.# [all] Builds all platforms for supplied version
Each platform is tagged <arch>-<version>.
version #.#.# <arch> Builds only the specified version and arch.
push #.#.# [as <tag>] Pushes locally-built versions as a multi-arch
manifest. If \`as <tag>\` is specified,
pushes the manifest using that tag instead.
Example workflow:
$0 clean *2.9.7*
$0 version 2.9.7 all
<test, then>
$0 push 2.9.7
$0 push 2.9.7 as 2.9
$0 push 2.9.7 as 2
$0 push 2.9.7 as latest
\`docker buildx\` commands:
buildx #.#.# Builds *and pushes* all platforms for supplied
version, using docker buildx. Built images must
be retrieved with \`docker pull\` for local use.
buildx #.#.# as <tag>
Builds and pushes all platforms for supplied
version, using docker buildx, tagging the
manifest with the supplied <tag>.
Example workflow:
$0 clean *2.9.7*
$0 buildx 2.9.7
$0 buildx 2.9.7 as 2.9
$0 buildx 2.9.7 as 2
$0 buildx 2.9.7 as latest
docker manifest inspect apache/couchdb:2.9.7
docker pull <--platform linux/other-arch> apache/couchdb:2.9.7 (for testing)
NOTE: Requires Docker 19.03+ with experimental features enabled.
Add { "experimental" : "true" } to /etc/docker/daemon.json, then
add { "experimental": "enabled" } to ~/.docker/config.json, then
restart the Docker daemon.
EOF
exit 0
}
# #######################
# handle -f/-n anywhere they appear on the CLI
POSITIONAL=()
while [[ $# -gt 0 ]]
do
# otherwise, we WILL match a regex against top-level directories!
set -f
key="$1"
case $key in
-f|--force)
unset PROMPT
shift
;;
-n|--no-qemu)
unset QEMU
shift
;;
*)
POSITIONAL+=("$1")
shift
;;
esac
set +f
done
# re-set all other arguments into argc
set -- "${POSITIONAL[@]}" # restore positional parameters
case "$1" in
clean)
# removes local images for a given version (and optionally platform)
shift
set -f
clean $*
set +f
;;
version)
# builds a specific version using docker build
# validate/reinstall QEMU
if [ ${QEMU} ]
then
update_qemu
fi
shift
if [ $# -lt 1 -o $# -gt 3 ]
then
usage
fi
# version #.#.# all
if [ "$2" = "all" ]
then
# build all the platforms and test them locally
build-all $1
else
# build a specific platform locally
build $1 $2
fi
;;
push)
# pushes already built local versions as manifest
shift
if [ $# -ne 1 -a $# -ne 3 ]
then
usage
fi
if [ $# -eq 1 ]
then
push $1
elif [ $2 = "as" ]
then
push $1 $3
else
usage
fi
;;
buildx)
# builds and pushes using docker buildx
if [ ${QEMU} ]
then
update_qemu
fi
shift
if [ $# -ne 1 -a $# -ne 3 ]
then
usage
fi
if [ $# -eq 1 ]
then
buildx $1
elif [ $2 = "as" ]
then
buildx $1 $3
else
usage
fi
;;
usage)
usage
;;
*)
usage
;;
esac