diff --git a/build-support/docker/Release.dockerfile b/build-support/docker/Release.dockerfile new file mode 100644 index 0000000000..589a0aeccc --- /dev/null +++ b/build-support/docker/Release.dockerfile @@ -0,0 +1,62 @@ +# This Dockerfile creates a production release image for the project. This +# downloads the release from releases.hashicorp.com and therefore requires that +# the release is published before building the Docker image. +# +# We don't rebuild the software because we want the exact checksums and +# binary signatures to match the software and our builds aren't fully +# reproducible currently. +FROM alpine:3.8 + +# NAME and VERSION are the name of the software in releases.hashicorp.com +# and the version to download. Example: NAME=consul VERSION=1.2.3. +ARG NAME +ARG VERSION + +# Set ARGs as ENV so that they can be used in ENTRYPOINT/CMD +ENV NAME=$NAME +ENV VERSION=$VERSION + +# This is the location of the releases. +ENV HASHICORP_RELEASES=https://releases.hashicorp.com + +# Create a non-root user to run the software. +RUN addgroup ${NAME} && \ + adduser -S -G ${NAME} ${NAME} + +# Set up certificates, base tools, and software. +RUN set -eux && \ + apk add --no-cache ca-certificates curl gnupg libcap openssl su-exec iputils && \ + BUILD_GPGKEY=91A6E7F85D05C65630BEF18951852D87348FFC4C; \ + found=''; \ + for server in \ + hkp://p80.pool.sks-keyservers.net:80 \ + hkp://keyserver.ubuntu.com:80 \ + hkp://pgp.mit.edu:80 \ + ; do \ + echo "Fetching GPG key $BUILD_GPGKEY from $server"; \ + gpg --keyserver "$server" --recv-keys "$BUILD_GPGKEY" && found=yes && break; \ + done; \ + test -z "$found" && echo >&2 "error: failed to fetch GPG key $BUILD_GPGKEY" && exit 1; \ + mkdir -p /tmp/build && \ + cd /tmp/build && \ + apkArch="$(apk --print-arch)" && \ + case "${apkArch}" in \ + aarch64) ARCH='arm64' ;; \ + armhf) ARCH='arm' ;; \ + x86) ARCH='386' ;; \ + x86_64) ARCH='amd64' ;; \ + *) echo >&2 "error: unsupported architecture: ${apkArch} (see ${HASHICORP_RELEASES}/${NAME}/${VERSION}/)" && exit 1 ;; \ + esac && \ + wget ${HASHICORP_RELEASES}/${NAME}/${VERSION}/${NAME}_${VERSION}_linux_${ARCH}.zip && \ + wget ${HASHICORP_RELEASES}/${NAME}/${VERSION}/${NAME}_${VERSION}_SHA256SUMS && \ + wget ${HASHICORP_RELEASES}/${NAME}/${VERSION}/${NAME}_${VERSION}_SHA256SUMS.sig && \ + gpg --batch --verify ${NAME}_${VERSION}_SHA256SUMS.sig ${NAME}_${VERSION}_SHA256SUMS && \ + grep ${NAME}_${VERSION}_linux_${ARCH}.zip ${NAME}_${VERSION}_SHA256SUMS | sha256sum -c && \ + unzip -d /bin ${NAME}_${VERSION}_linux_${ARCH}.zip && \ + cd /tmp && \ + rm -rf /tmp/build && \ + apk del gnupg openssl && \ + rm -rf /root/.gnupg + +USER ${NAME} +CMD /bin/${NAME} diff --git a/build-support/docker/hooks/build b/build-support/docker/hooks/build new file mode 100755 index 0000000000..6074dd6a2d --- /dev/null +++ b/build-support/docker/hooks/build @@ -0,0 +1,28 @@ +#!/bin/bash +set -e + +# DOCKER_REPO is like "index.docker.io/foo/bar". The bashism below extracts +# the string after the last "/". For VERSION, we extract the string after the +# first "v" (so v1.2.3 turns into 1.2.3). +NAME=${DOCKER_REPO##*/} +VERSION=${SOURCE_BRANCH#*v} + +# If the version is equal to the original value, then we have an invalid +# branch name. In this case, we trigger a Dev build. +if [ "${VERSION}" = "${SOURCE_BRANCH}" ]; then + echo "=> Building full dev image for branch ${SOURCE_BRANCH}..." + docker build \ + -t ${IMAGE_NAME} \ + -f $(basename "${BUILD_PATH}") \ + ../.. + exit 0 +fi + +# We have a NAME and VERSION set, so we build a release image. +echo "=> Building Docker image for ${NAME}:${VERSION}" +docker build \ + -t ${DOCKER_REPO}:${VERSION} \ + -f $(basename "${BUILD_PATH}") \ + --build-arg "NAME=${NAME}" \ + --build-arg "VERSION=${VERSION}" \ + . diff --git a/build-support/functions/40-publish.sh b/build-support/functions/40-publish.sh index e6d42c677d..c902180fba 100644 --- a/build-support/functions/40-publish.sh +++ b/build-support/functions/40-publish.sh @@ -9,13 +9,13 @@ function hashicorp_release { # Notes: # Requires the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables # to be set - + status "Uploading files" hc-releases upload "${1}" || return 1 - + status "Publishing the release" hc-releases publish || return 1 - + return 0 } @@ -28,15 +28,15 @@ function confirm_git_remote { # 0 - success # * - error # - + local remote="$2" local url=$(git_remote_url "$1" "${remote}") - + echo -e "\n\nConfigured Git Remote: ${remote}" echo -e "Configured Git URL: ${url}\n" - + local answer="" - + while true do case "${answer}" in @@ -65,25 +65,25 @@ function confirm_git_push_changes { # 0 - success # * - error # - + if ! test -d "$1" then - err "ERROR: '$1' is not a directory. confirm_git_push_changes must be called with the path to a git repo as the first argument'" + err "ERROR: '$1' is not a directory. confirm_git_push_changes must be called with the path to a git repo as the first argument'" return 1 fi - + pushd "${1}" > /dev/null - - + + declare -i ret=0 git_log_summary || ret=1 if test ${ret} -eq 0 then # put a empty line between the git changes and the prompt echo "" - + local answer="" - + while true do case "${answer}" in @@ -108,7 +108,7 @@ function confirm_git_push_changes { esac done fi - + popd > /dev/null return $ret } @@ -121,18 +121,18 @@ function extract_consul_local { # Returns: # 0 - success # * - error - + local zfile="${1}/${CONSUL_PKG_NAME}_${2}_$(go env GOOS)_$(go env GOARCH).zip" - + if ! test -f "${zfile}" then err "ERROR: File not found or is not a regular file: ${zfile}" return 1 fi - + local ret=0 local tfile="$(mktemp) -t "${CONSUL_PKG_NAME}_")" - + unzip -p "${zfile}" "consul" > "${tfile}" if test $? -eq 0 then @@ -148,25 +148,25 @@ function extract_consul_local { function confirm_consul_version { # Arguments: # $1 - consul exe to use - # + # # Returns: # 0 - success # * - error local consul_exe="$1" - + if ! test -x "${consul_exe}" then err "ERROR: '${consul_exe} is not an executable" return 1 fi - + "${consul_exe}" version - + # put a empty line between the version output and the prompt echo "" - + local answer="" - + while true do case "${answer}" in @@ -194,7 +194,7 @@ function confirm_consul_info { # Returns: # 0 - success # * - error - + local consul_exe="$1" local log_file="$(mktemp) -t "consul_log_")" "${consul_exe}" agent -dev > "${log_file}" 2>&1 & @@ -202,11 +202,11 @@ function confirm_consul_info { sleep 1 status "First 25 lines/1s of the agents output:" head -n 25 "${log_file}" - + echo "" local ret=0 local answer="" - + while true do case "${answer}" in @@ -224,14 +224,14 @@ function confirm_consul_info { ;; esac done - + if test "${ret}" -eq 0 then status "Consul Info Output" "${consul_exe}" info echo "" local answer="" - + while true do case "${answer}" in @@ -250,7 +250,7 @@ function confirm_consul_info { esac done fi - + if test "${ret}" -eq 0 then local tfile="$(mktemp) -t "${CONSUL_PKG_NAME}_")" @@ -259,7 +259,7 @@ function confirm_consul_info { err "ERROR: Failed to curl http://localhost:8500/ui/" return 1 fi - + local ui_vers=$(ui_version "${tfile}") if test $? -ne 0 then @@ -274,10 +274,10 @@ function confirm_consul_info { return 1 fi status "UI Logo: ${ui_logo_type}" - + echo "" local answer="" - + while true do case "${answer}" in @@ -296,13 +296,13 @@ function confirm_consul_info { esac done fi - - + + status "Requesting Consul to leave the cluster / shutdown" "${consul_exe}" leave wait ${consul_pid} > /dev/null 2>&1 - + return $? } @@ -318,38 +318,38 @@ function verify_release_build { # Returns: # 0 - success # * - failure - + if ! test -d "$1" then - err "ERROR: '$1' is not a directory. publish_release must be called with the path to the top level source as the first argument'" + err "ERROR: '$1' is not a directory. publish_release must be called with the path to the top level source as the first argument'" return 1 fi - + local sdir="$1" - + local vers="$(get_version ${sdir} true false)" if test -n "$2" then vers="$2" fi - + if test -z "${vers}" then - err "Please specify a version (couldn't parse one from the source)." + err "Please specify a version (couldn't parse one from the source)." return 1 fi - + status_stage "==> Verifying release files" check_release "${sdir}/pkg/dist" "${vers}" true || return 1 - + status_stage "==> Extracting Consul version for local system" local consul_exe=$(extract_consul "${sdir}/pkg/dist" "${vers}") || return 1 # make sure to remove the temp file trap "rm '${consul_exe}'" EXIT - + status_stage "==> Confirming Consul Version" confirm_consul_version "${consul_exe}" || return 1 - + status_stage "==> Confirming Consul Agent Info" confirm_consul_info "${consul_exe}" || return 1 } @@ -363,61 +363,64 @@ function publish_release { # Returns: # 0 - success # * - error - + if ! test -d "$1" then - err "ERROR: '$1' is not a directory. publish_release must be called with the path to the top level source as the first argument'" + err "ERROR: '$1' is not a directory. publish_release must be called with the path to the top level source as the first argument'" return 1 fi - + local sdir="$1" local pub_git="$2" local pub_hc_releases="$3" - + if test -z "${pub_git}" then pub_git=1 fi - + if test -z "${pub_hc_releases}" then pub_hc_releases=1 fi - + local vers="$(get_version ${sdir} true false)" if test $? -ne 0 then - err "Please specify a version (couldn't parse one from the source)." + err "Please specify a version (couldn't parse one from the source)." return 1 fi - + verify_release_build "$1" "${vers}" || return 1 - + status_stage "==> Confirming Git is clean" is_git_clean "$1" true || return 1 status_stage "==> Confirming Git Changes" confirm_git_push_changes "$1" || return 1 - + status_stage "==> Checking for blacklisted Git Remote" local remote=$(find_git_remote "${sdir}") || return 1 git_remote_not_blacklisted "${sdir}" "${remote}" || return 1 - + status_stage "==> Confirming Git Remote" confirm_git_remote "${sdir}" "${remote}" || return 1 - + + + if is_set "${pub_hc_releases}" + then + status_stage "==> Publishing to releases.hashicorp.com" + hashicorp_release "${sdir}/pkg/dist" || return 1 + fi + + # We must publish to releases.hashicorp.com first above so that any + # Docker automated builds from pushing the tag can retrieve the proper + # version. if is_set "${pub_git}" then status_stage "==> Pushing to Git" git_push_ref "$1" "" "${remote}" || return 1 git_push_ref "$1" "v${vers}" "${remote}" || return 1 fi - - if is_set "${pub_hc_releases}" - then - status_stage "==> Publishing to releases.hashicorp.com" - hashicorp_release "${sdir}/pkg/dist" || return 1 - fi - - return 0 + return 0 }