From 8390fd856922deec4ca7bf0004e6e8e81f60c9f2 Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Wed, 7 Jun 2017 16:31:50 -0700 Subject: [PATCH] Add multiarch support (again) and update to 17.06.0-ce-rc2 --- .architectures-lib | 60 ++++++++++++++++++++++++++++++++++ 17.03-rc/Dockerfile | 20 +++++++++++- 17.03-rc/release-architectures | 4 +++ 17.03/Dockerfile | 20 +++++++++++- 17.03/release-architectures | 4 +++ 17.05/Dockerfile | 20 +++++++++++- 17.05/release-architectures | 4 +++ 17.06-rc/Dockerfile | 23 +++++++++++-- 17.06-rc/release-architectures | 9 +++++ Dockerfile-dind.template | 35 ++++++++++++++++++++ Dockerfile-git.template | 5 +++ Dockerfile.template | 47 ++++++++++++++++++++++++++ docker-entrypoint.sh | 20 ++++++++++++ dockerd-entrypoint.sh | 21 ++++++++++++ generate-stackbrew-library.sh | 6 ++++ update.sh | 58 ++++++++++++++++++++++++++------ 16 files changed, 341 insertions(+), 15 deletions(-) create mode 100644 .architectures-lib create mode 100644 17.03-rc/release-architectures create mode 100644 17.03/release-architectures create mode 100644 17.05/release-architectures create mode 100644 17.06-rc/release-architectures create mode 100644 Dockerfile-dind.template create mode 100644 Dockerfile-git.template create mode 100644 Dockerfile.template create mode 100755 docker-entrypoint.sh create mode 100755 dockerd-entrypoint.sh diff --git a/.architectures-lib b/.architectures-lib new file mode 100644 index 000000000..d748fde73 --- /dev/null +++ b/.architectures-lib @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +_awkArch() { + local version="$1"; shift + local awkExpr="$1"; shift + awk "$@" "/^#|^\$/ { next } $awkExpr" "$version/release-architectures" +} + +apkArches() { + local version="$1"; shift + _awkArch "$version" '{ print $2 }' +} + +hasBashbrewArch() { + local version="$1"; shift + local bashbrewArch="$1"; shift + _awkArch "$version" 'BEGIN { exitCode = 1 } $1 == bashbrewArch { exitCode = 0 } END { exit exitCode }' -v bashbrewArch="$bashbrewArch" +} + +apkToDockerArch() { + local version="$1"; shift + local apkArch="$1"; shift + _awkArch "$version" '$2 == apkArch { print $3; exit }' -v apkArch="$apkArch" +} + +_generateParentRepoToArches() { + local repo="$1"; shift + local officialImagesUrl='https://github.com/docker-library/official-images/raw/master/library/' + + eval "declare -g -A parentRepoToArches=( $( + find -name 'Dockerfile' -exec awk ' + toupper($1) == "FROM" && $2 !~ /^('"$repo"'|scratch|microsoft\/[^:]+)(:|$)/ { + print "'"$officialImagesUrl"'" $2 + } + ' '{}' + \ + | sort -u \ + | xargs bashbrew cat --format '[{{ .RepoName }}:{{ .TagName }}]="{{ join " " .TagEntry.Architectures }}"' + ) )" +} +_generateParentRepoToArches 'docker' + +parentArches() { + local version="$1"; shift # "17.06", etc + + local parent="$(awk 'toupper($1) == "FROM" { print $2 }' "$version/Dockerfile")" + echo "${parentRepoToArches[$parent]:-}" +} +versionArches() { + local version="$1"; shift + + local parentArches="$(parentArches "$version")" + + local versionArches=() + for arch in $parentArches; do + if hasBashbrewArch "$version" "$arch"; then + versionArches+=( "$arch" ) + fi + done + echo "${versionArches[*]}" +} diff --git a/17.03-rc/Dockerfile b/17.03-rc/Dockerfile index 0ca1d7829..e4f528dc2 100644 --- a/17.03-rc/Dockerfile +++ b/17.03-rc/Dockerfile @@ -6,6 +6,8 @@ RUN apk add --no-cache \ ENV DOCKER_CHANNEL test ENV DOCKER_VERSION 17.03.2-ce-rc1 # TODO ENV DOCKER_SHA256 +# https://github.com/docker/docker-ce/blob/5b073ee2cf564edee5adca05eee574142f7627bb/components/packaging/static/hash_files !! +# (no SHA file artifacts on download.docker.com yet as of 2017-06-07 though) RUN set -ex; \ # why we use "curl" instead of "wget": @@ -16,14 +18,30 @@ RUN set -ex; \ curl \ tar \ ; \ - curl -fL -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/x86_64/docker-${DOCKER_VERSION}.tgz"; \ + \ +# this "case" statement is generated via "update.sh" + apkArch="$(apk --print-arch)"; \ + case "$apkArch" in \ + x86_64) dockerArch='x86_64' ;; \ + *) echo >&2 "error: unsupported architecture ($apkArch)"; exit 1 ;;\ + esac; \ + \ + if ! curl -fL -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${dockerArch}/docker-${DOCKER_VERSION}-${dockerArch}.tgz"; then \ + if ! curl -fL -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${dockerArch}/docker-${DOCKER_VERSION}.tgz"; then \ + echo >&2 "error: failed to download 'docker-${DOCKER_VERSION}' from '${DOCKER_CHANNEL}' for '${dockerArch}'"; \ + exit 1; \ + fi; \ + fi; \ + \ tar --extract \ --file docker.tgz \ --strip-components 1 \ --directory /usr/local/bin/ \ ; \ rm docker.tgz; \ + \ apk del .fetch-deps; \ + \ dockerd -v; \ docker -v diff --git a/17.03-rc/release-architectures b/17.03-rc/release-architectures new file mode 100644 index 000000000..4ca09e6a9 --- /dev/null +++ b/17.03-rc/release-architectures @@ -0,0 +1,4 @@ +# see subdirectories of https://download.docker.com/linux/static/ + +# bashbrew-arch apk-arch docker-release-arch +amd64 x86_64 x86_64 diff --git a/17.03/Dockerfile b/17.03/Dockerfile index 710ed71f2..e1c29f63c 100644 --- a/17.03/Dockerfile +++ b/17.03/Dockerfile @@ -6,6 +6,8 @@ RUN apk add --no-cache \ ENV DOCKER_CHANNEL stable ENV DOCKER_VERSION 17.03.1-ce # TODO ENV DOCKER_SHA256 +# https://github.com/docker/docker-ce/blob/5b073ee2cf564edee5adca05eee574142f7627bb/components/packaging/static/hash_files !! +# (no SHA file artifacts on download.docker.com yet as of 2017-06-07 though) RUN set -ex; \ # why we use "curl" instead of "wget": @@ -16,14 +18,30 @@ RUN set -ex; \ curl \ tar \ ; \ - curl -fL -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/x86_64/docker-${DOCKER_VERSION}.tgz"; \ + \ +# this "case" statement is generated via "update.sh" + apkArch="$(apk --print-arch)"; \ + case "$apkArch" in \ + x86_64) dockerArch='x86_64' ;; \ + *) echo >&2 "error: unsupported architecture ($apkArch)"; exit 1 ;;\ + esac; \ + \ + if ! curl -fL -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${dockerArch}/docker-${DOCKER_VERSION}-${dockerArch}.tgz"; then \ + if ! curl -fL -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${dockerArch}/docker-${DOCKER_VERSION}.tgz"; then \ + echo >&2 "error: failed to download 'docker-${DOCKER_VERSION}' from '${DOCKER_CHANNEL}' for '${dockerArch}'"; \ + exit 1; \ + fi; \ + fi; \ + \ tar --extract \ --file docker.tgz \ --strip-components 1 \ --directory /usr/local/bin/ \ ; \ rm docker.tgz; \ + \ apk del .fetch-deps; \ + \ dockerd -v; \ docker -v diff --git a/17.03/release-architectures b/17.03/release-architectures new file mode 100644 index 000000000..4ca09e6a9 --- /dev/null +++ b/17.03/release-architectures @@ -0,0 +1,4 @@ +# see subdirectories of https://download.docker.com/linux/static/ + +# bashbrew-arch apk-arch docker-release-arch +amd64 x86_64 x86_64 diff --git a/17.05/Dockerfile b/17.05/Dockerfile index 78a36999d..35a58fce0 100644 --- a/17.05/Dockerfile +++ b/17.05/Dockerfile @@ -6,6 +6,8 @@ RUN apk add --no-cache \ ENV DOCKER_CHANNEL edge ENV DOCKER_VERSION 17.05.0-ce # TODO ENV DOCKER_SHA256 +# https://github.com/docker/docker-ce/blob/5b073ee2cf564edee5adca05eee574142f7627bb/components/packaging/static/hash_files !! +# (no SHA file artifacts on download.docker.com yet as of 2017-06-07 though) RUN set -ex; \ # why we use "curl" instead of "wget": @@ -16,14 +18,30 @@ RUN set -ex; \ curl \ tar \ ; \ - curl -fL -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/x86_64/docker-${DOCKER_VERSION}.tgz"; \ + \ +# this "case" statement is generated via "update.sh" + apkArch="$(apk --print-arch)"; \ + case "$apkArch" in \ + x86_64) dockerArch='x86_64' ;; \ + *) echo >&2 "error: unsupported architecture ($apkArch)"; exit 1 ;;\ + esac; \ + \ + if ! curl -fL -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${dockerArch}/docker-${DOCKER_VERSION}-${dockerArch}.tgz"; then \ + if ! curl -fL -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${dockerArch}/docker-${DOCKER_VERSION}.tgz"; then \ + echo >&2 "error: failed to download 'docker-${DOCKER_VERSION}' from '${DOCKER_CHANNEL}' for '${dockerArch}'"; \ + exit 1; \ + fi; \ + fi; \ + \ tar --extract \ --file docker.tgz \ --strip-components 1 \ --directory /usr/local/bin/ \ ; \ rm docker.tgz; \ + \ apk del .fetch-deps; \ + \ dockerd -v; \ docker -v diff --git a/17.05/release-architectures b/17.05/release-architectures new file mode 100644 index 000000000..4ca09e6a9 --- /dev/null +++ b/17.05/release-architectures @@ -0,0 +1,4 @@ +# see subdirectories of https://download.docker.com/linux/static/ + +# bashbrew-arch apk-arch docker-release-arch +amd64 x86_64 x86_64 diff --git a/17.06-rc/Dockerfile b/17.06-rc/Dockerfile index bb1888637..1539b96db 100644 --- a/17.06-rc/Dockerfile +++ b/17.06-rc/Dockerfile @@ -4,8 +4,10 @@ RUN apk add --no-cache \ ca-certificates ENV DOCKER_CHANNEL test -ENV DOCKER_VERSION 17.06.0-ce-rc1 +ENV DOCKER_VERSION 17.06.0-ce-rc2 # TODO ENV DOCKER_SHA256 +# https://github.com/docker/docker-ce/blob/5b073ee2cf564edee5adca05eee574142f7627bb/components/packaging/static/hash_files !! +# (no SHA file artifacts on download.docker.com yet as of 2017-06-07 though) RUN set -ex; \ # why we use "curl" instead of "wget": @@ -16,14 +18,31 @@ RUN set -ex; \ curl \ tar \ ; \ - curl -fL -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/x86_64/docker-${DOCKER_VERSION}.tgz"; \ + \ +# this "case" statement is generated via "update.sh" + apkArch="$(apk --print-arch)"; \ + case "$apkArch" in \ + x86_64) dockerArch='x86_64' ;; \ + s390x) dockerArch='s390x' ;; \ + *) echo >&2 "error: unsupported architecture ($apkArch)"; exit 1 ;;\ + esac; \ + \ + if ! curl -fL -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${dockerArch}/docker-${DOCKER_VERSION}-${dockerArch}.tgz"; then \ + if ! curl -fL -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${dockerArch}/docker-${DOCKER_VERSION}.tgz"; then \ + echo >&2 "error: failed to download 'docker-${DOCKER_VERSION}' from '${DOCKER_CHANNEL}' for '${dockerArch}'"; \ + exit 1; \ + fi; \ + fi; \ + \ tar --extract \ --file docker.tgz \ --strip-components 1 \ --directory /usr/local/bin/ \ ; \ rm docker.tgz; \ + \ apk del .fetch-deps; \ + \ dockerd -v; \ docker -v diff --git a/17.06-rc/release-architectures b/17.06-rc/release-architectures new file mode 100644 index 000000000..5f17a4792 --- /dev/null +++ b/17.06-rc/release-architectures @@ -0,0 +1,9 @@ +# see subdirectories of https://download.docker.com/linux/static/ + +# bashbrew-arch apk-arch docker-release-arch +amd64 x86_64 x86_64 +s390x s390x s390x + +# Docker's "armhf" is ARMv7 (as of 2017-06-07, 17.06.0-ce-rc2) +#arm32v6 armhf armhf +# https://dockercommunity.slackarchive.io/arm/page-13 diff --git a/Dockerfile-dind.template b/Dockerfile-dind.template new file mode 100644 index 000000000..435da815b --- /dev/null +++ b/Dockerfile-dind.template @@ -0,0 +1,35 @@ +FROM docker:%%VERSION%% + +# https://github.com/docker/docker/blob/master/project/PACKAGERS.md#runtime-dependencies +RUN apk add --no-cache \ + btrfs-progs \ + e2fsprogs \ + e2fsprogs-extra \ + iptables \ + xfsprogs \ + xz + +# TODO aufs-tools + +# set up subuid/subgid so that "--userns-remap=default" works out-of-the-box +RUN set -x \ + && addgroup -S dockremap \ + && adduser -S -G dockremap dockremap \ + && echo 'dockremap:165536:65536' >> /etc/subuid \ + && echo 'dockremap:165536:65536' >> /etc/subgid + +ENV DIND_COMMIT 3b5fac462d21ca164b3778647420016315289034 + +RUN set -ex; \ + apk add --no-cache --virtual .fetch-deps libressl; \ + wget -O /usr/local/bin/dind "https://raw.githubusercontent.com/docker/docker/${DIND_COMMIT}/hack/dind"; \ + chmod +x /usr/local/bin/dind; \ + apk del .fetch-deps + +COPY dockerd-entrypoint.sh /usr/local/bin/ + +VOLUME /var/lib/docker +EXPOSE 2375 + +ENTRYPOINT ["dockerd-entrypoint.sh"] +CMD [] diff --git a/Dockerfile-git.template b/Dockerfile-git.template new file mode 100644 index 000000000..25ed08cd2 --- /dev/null +++ b/Dockerfile-git.template @@ -0,0 +1,5 @@ +FROM docker:%%VERSION%% + +RUN apk add --no-cache \ + git \ + openssh-client diff --git a/Dockerfile.template b/Dockerfile.template new file mode 100644 index 000000000..ba5d088ac --- /dev/null +++ b/Dockerfile.template @@ -0,0 +1,47 @@ +FROM alpine:%%ALPINE-VERSION%% + +RUN apk add --no-cache \ + ca-certificates + +ENV DOCKER_CHANNEL %%DOCKER-CHANNEL%% +ENV DOCKER_VERSION %%DOCKER-VERSION%% +# TODO ENV DOCKER_SHA256 +# https://github.com/docker/docker-ce/blob/5b073ee2cf564edee5adca05eee574142f7627bb/components/packaging/static/hash_files !! +# (no SHA file artifacts on download.docker.com yet as of 2017-06-07 though) + +RUN set -ex; \ +# why we use "curl" instead of "wget": +# + wget -O docker.tgz https://download.docker.com/linux/static/stable/x86_64/docker-17.03.1-ce.tgz +# Connecting to download.docker.com (54.230.87.253:443) +# wget: error getting response: Connection reset by peer + apk add --no-cache --virtual .fetch-deps \ + curl \ + tar \ + ; \ + \ +# this "case" statement is generated via "update.sh" + %%ARCH-CASE%%; \ + \ + if ! curl -fL -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${dockerArch}/docker-${DOCKER_VERSION}-${dockerArch}.tgz"; then \ + if ! curl -fL -o docker.tgz "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/${dockerArch}/docker-${DOCKER_VERSION}.tgz"; then \ + echo >&2 "error: failed to download 'docker-${DOCKER_VERSION}' from '${DOCKER_CHANNEL}' for '${dockerArch}'"; \ + exit 1; \ + fi; \ + fi; \ + \ + tar --extract \ + --file docker.tgz \ + --strip-components 1 \ + --directory /usr/local/bin/ \ + ; \ + rm docker.tgz; \ + \ + apk del .fetch-deps; \ + \ + dockerd -v; \ + docker -v + +COPY docker-entrypoint.sh /usr/local/bin/ + +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["sh"] diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 000000000..fe790b143 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,20 @@ +#!/bin/sh +set -e + +# first arg is `-f` or `--some-option` +if [ "${1#-}" != "$1" ]; then + set -- docker "$@" +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 help "$1" > /dev/null 2>&1; then + set -- docker "$@" +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 "$@" diff --git a/dockerd-entrypoint.sh b/dockerd-entrypoint.sh new file mode 100755 index 000000000..884c4b4d9 --- /dev/null +++ b/dockerd-entrypoint.sh @@ -0,0 +1,21 @@ +#!/bin/sh +set -e + +# no arguments passed +# or first arg is `-f` or `--some-option` +if [ "$#" -eq 0 -o "${1#-}" != "$1" ]; then + # add our default arguments + set -- dockerd \ + --host=unix:///var/run/docker.sock \ + --host=tcp://0.0.0.0:2375 \ + --storage-driver=vfs \ + "$@" +fi + +if [ "$1" = 'dockerd' ]; then + # if we're running Docker, let's pipe through dind + # (and we'll run dind explicitly with "sh" since its shebang is /bin/bash) + set -- sh "$(which dind)" "$@" +fi + +exec "$@" diff --git a/generate-stackbrew-library.sh b/generate-stackbrew-library.sh index a81babdfe..e12e50eee 100755 --- a/generate-stackbrew-library.sh +++ b/generate-stackbrew-library.sh @@ -11,6 +11,8 @@ declare -A aliases=( self="$(basename "$BASH_SOURCE")" cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" +source '.architectures-lib' + versions=( */ ) versions=( "${versions[@]%/}" ) @@ -71,9 +73,12 @@ for version in "${versions[@]}"; do ${aliases[$version]:-} ) + versionArches="$(versionArches "$version")" + echo cat <<-EOE Tags: $(join ', ' "${versionAliases[@]}") + Architectures: $(join ', ' $versionArches) GitCommit: $commit Directory: $version EOE @@ -92,6 +97,7 @@ for version in "${versions[@]}"; do echo cat <<-EOE Tags: $(join ', ' "${variantAliases[@]}") + Architectures: $(join ', ' $versionArches) GitCommit: $commit Directory: $version/$variant EOE diff --git a/update.sh b/update.sh index ad6b904b9..d61bd13eb 100755 --- a/update.sh +++ b/update.sh @@ -1,21 +1,43 @@ #!/bin/bash set -eo pipefail +defaultAlpineVersion='3.6' +declare -A alpineVersion=( + [17.03]='3.5' + [17.03-rc]='3.5' + [17.05]='3.5' +) + cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" +source '.architectures-lib' + versions=( "$@" ) if [ ${#versions[@]} -eq 0 ]; then versions=( */ ) fi versions=( "${versions[@]%/}" ) +# see http://stackoverflow.com/a/2705678/433558 +sed_escape_lhs() { + echo "$@" | sed -e 's/[]\/$*.^|[]/\\&/g' +} +sed_escape_rhs() { + echo "$@" | sed -e 's/[\/&]/\\&/g' | sed -e ':a;N;$!ba;s/\n/\\n/g' +} + # "tac|tac" for http://stackoverflow.com/a/28879552/433558 dindLatest="$(curl -fsSL 'https://github.com/docker/docker/commits/master/hack/dind.atom' | tac|tac | awk -F '[[:space:]]*[<>/]+' '$2 == "id" && $3 ~ /Commit/ { print $4; exit }')" +# TODO once "Supported Docker versions" minimums at Docker 1.8+ (1.6 at time of this writing), bring this back again +#sed -r -e 's/^(ENV DIND_COMMIT) .*/\1 '"$dindLatest"'/' Dockerfile-dind.template + dockerVersions="$( { git ls-remote --tags https://github.com/docker/docker-ce.git - git ls-remote --tags https://github.com/docker/docker.git # TODO remove-me (17.06+ live in docker-ce) + + # TODO remove-me (17.06+ live exclusively in docker-ce) + git ls-remote --tags https://github.com/docker/docker.git } \ | cut -d$'\t' -f2 \ | grep '^refs/tags/v[0-9].*$' \ @@ -47,15 +69,31 @@ for version in "${versions[@]}"; do channel='stable' fi - ( - set -x - #s/^(ENV DIND_COMMIT) .*/\1 '"$dindLatest"'/; # TODO once "Supported Docker versions" minimums at Docker 1.8+ (1.6 at time of this writing), bring this back again - sed -ri \ - -e 's/^(ENV DOCKER_CHANNEL) .*/\1 '"$channel"'/' \ - -e 's/^(ENV DOCKER_VERSION) .*/\1 '"$fullVersion"'/' \ - -e 's/^(FROM docker):.*/\1:'"$version"'/' \ - "$version"/{,git/,dind/}Dockerfile - ) + archCase='apkArch="$(apk --print-arch)"; '$'\\\n' + archCase+=$'\t''case "$apkArch" in '$'\\\n' + for apkArch in $(apkArches "$version"); do + dockerArch="$(apkToDockerArch "$version" "$apkArch")" + archCase+=$'\t\t'"$apkArch) dockerArch='$dockerArch' ;; "$'\\\n' + done + archCase+=$'\t\t''*) echo >&2 "error: unsupported architecture ($apkArch)"; exit 1 ;;'$'\\\n' + archCase+=$'\t''esac' + + alpine="${alpineVersion[$version]:-$defaultAlpineVersion}" + + sed -r \ + -e 's!%%DOCKER-CHANNEL%%!'"$channel"'!g' \ + -e 's!%%DOCKER-VERSION%%!'"$fullVersion"'!g' \ + -e 's!%%ALPINE-VERSION%%!'"$alpine"'!g' \ + -e 's!%%ARCH-CASE%%!'"$(sed_escape_rhs "$archCase")"'!g' \ + Dockerfile.template > "$version/Dockerfile" + cp -a docker-entrypoint.sh "$version/" + cp -a dockerd-entrypoint.sh "$version/dind/" + + for variant in git dind; do + sed -r \ + -e 's!%%VERSION%%!'"$version"'!g' \ + "Dockerfile-$variant.template" > "$version/$variant/Dockerfile" + done travisEnv='\n - VERSION='"$version$travisEnv" done