| #!/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 |