From 5c8ff928babf15ed929a691a5dd425f08e5d9c1a Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Fri, 28 Jul 2017 17:30:42 -0700 Subject: [PATCH] build: scripted local build process for docker images #15596 set things up with the intent that the docker image build process would be handled by the automated build system on dockerhub, but after merging we found that it's impossible to change the source git repository for an existing dockerhub repository. To get away from the limitations of dockerhub, we intend to eventually automate these builds in a separate CI system. Here we add some scripts that would drive such an automated process. It's split into multiple steps to allow for situations where the new version should not be tagged as the latest, and to make it easier and safer to test the build script while doing development on it. Since this automated process doesn't yet exist, a wrapper script release.sh is included to help run a local, manual build and deploy process in the mean time. The README.md in the docker-release dir here contains details on the intended usage. --- scripts/docker-release/Dockerfile-release | 2 + scripts/docker-release/README.md | 92 +++++++++++++++------- scripts/docker-release/build.sh | 34 +++++++++ scripts/docker-release/hooks/build | 18 ----- scripts/docker-release/push.sh | 20 +++++ scripts/docker-release/release.sh | 93 +++++++++++++++++++++++ scripts/docker-release/tag.sh | 26 +++++++ 7 files changed, 241 insertions(+), 44 deletions(-) create mode 100755 scripts/docker-release/build.sh delete mode 100755 scripts/docker-release/hooks/build create mode 100755 scripts/docker-release/push.sh create mode 100755 scripts/docker-release/release.sh create mode 100755 scripts/docker-release/tag.sh diff --git a/scripts/docker-release/Dockerfile-release b/scripts/docker-release/Dockerfile-release index f1600df77..4545d0a96 100644 --- a/scripts/docker-release/Dockerfile-release +++ b/scripts/docker-release/Dockerfile-release @@ -34,4 +34,6 @@ RUN echo Building image for Terraform ${TERRAFORM_VERSION} && \ unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip -d /bin && \ rm -f terraform_${TERRAFORM_VERSION}_linux_amd64.zip terraform_${TERRAFORM_VERSION}_SHA256SUMS* +LABEL "com.hashicorp.terraform.version"="${TERRAFORM_VERSION}" + ENTRYPOINT ["/bin/terraform"] diff --git a/scripts/docker-release/README.md b/scripts/docker-release/README.md index 2224aa6da..afcdfe4b6 100644 --- a/scripts/docker-release/README.md +++ b/scripts/docker-release/README.md @@ -1,37 +1,77 @@ # Terraform Docker Release Build -This directory contains configuration to drive the Dockerhub automated build -for Terraform. This is different than the root Dockerfile (which produces -the "full" image on Dockerhub) because it uses the release archives from -releases.hashicorp.com. It is therefore not possible to use this configuration -to build an image for a commit that hasn't been released. +This directory contains configuration to drive the docker image releases for +Terraform. -## How it works +Two different types of image are produced for each Terraform release: -Dockerhub runs the `hooks/build` script to trigger the build. That uses -`git describe` to identify the tag corresponding to the current `HEAD`. If -the current commit _isn't_ tagged with a version number corresponding to -a Terraform release already on releases.hashicorp.com, the build will fail. +* A "light" image that includes just the release binary that should match + what's on releases.hashicorp.com. -## What it produces +* A "full" image that contains all of the Terraform source code and a binary + built from that source. -This configuration is used to produce the "latest", "light", and "beta" -tags in Dockerhub, as well as specific version tags. +The latter can be produced for any arbitrary commit by running `docker build` +in the root of this repository. The former requires that the release archive +already be deployed on releases.hashicorp.com. -* "latest" and "light" are synonyms, and are built from a branch in this -repository called "stable". -* "beta" is built from a branch called "beta". +## Build and Release -All of these branches should be updated only to _tagged_ commits, and only when -it is desirable to create a new release image. +The scripts in this directory are intended for running the steps to build, +tag, and push the two images for a tagged and released version of Terraform. +They expect to be run with git `HEAD` pointed at a release tag, whose name +is used to determine the version to build. The version number indicated +by the tag that `HEAD` is pointed at will be referred to below as +the _current version_. -## The `full` and `master` images image +* `build.sh` builds locally both of the images for the current version. + This operates on the local docker daemon only, and produces tags that + include the current version number. -This configuration does not produce the "full" image. That is instead produced -by the `Dockerfile` in the repository root, driven by updates to the "stable" -branch. +* `tag.sh` updates the `latest`, `light` and `full` tags to refer to the + images for the current version, which must've been already produced by + an earlier run of `build.sh`. This operates on the local docker daemon + only. -The "master" tag is updated for _every_ commit to the master branch of -the Terraform core repository. It is not recommended to use these images for -any production use, but they can be useful for testing bleeding-edge features -that are not yet included in a release. +* `push.sh` pushes the current version tag and the `latest`, `light` and + `full` tags up to dockerhub for public consumption. This writes images + to dockerhub, and so it requires docker credentials that have access to + write into the `hashicorp/terraform` repository. + +### Releasing a new "latest" version + +In the common case where a release is going to be considered the new latest +stable version of Terraform, the helper script `release.sh` orchestrates +all of the necessary steps to release to dockerhub: + +``` +$ git checkout v0.10.0 +$ scripts/docker-release/release.sh +``` + +Behind the scenes this script is running `build.sh`, `tag.sh` and `push.sh` +as described above, with some extra confirmation steps to verify the +correctness of the build. + +This script is interactive and so isn't suitable for running in automation. +For automation, run the individual scripts directly. + +### Releasing a beta version or a patch to an earlier minor release + +The `release.sh` wrapper is not appropriate in two less common situations: + +* The version being released is a beta or other pre-release version, with + a version number like `v0.10.0-beta1` or `v0.10.0-rc1`. + +* The version being released belongs to a non-current minor release. For + example, if the current stable version is `v0.10.1` but the version + being released is `v0.9.14`. + +In both of these cases, only the specific version tag should be updated, +which can be done as follows: + +``` +$ git checkout v0.11.0-beta1 +$ scripts/docker-release/build.sh +$ docker push hashicorp/terraform:0.11.0-beta1 +``` diff --git a/scripts/docker-release/build.sh b/scripts/docker-release/build.sh new file mode 100755 index 000000000..8442e8a68 --- /dev/null +++ b/scripts/docker-release/build.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +# This script builds two docker images for the version referred to by the +# current git HEAD. +# +# After running this, run tag.sh if the images that are built should be +# tagged as the "latest" release. + +set -eu + +BASE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd "$BASE" + +if [ "$#" -eq 0 ]; then + # We assume that this is always running while git HEAD is pointed at a release + # tag or a branch that is pointed at the same commit as a release tag. If not, + # this will fail since we can't build a release image for a commit that hasn't + # actually been released. + VERSION="$(git describe)" +else + # This mode is here only to support release.sh, which ensures that the given + # version matches the current git tag. Running this script manually with + # an argument can't guarantee correct behavior since the "full" image + # will be built against the current work tree regardless of which version + # is selected. + VERSION="$1" +fi + +echo "-- Building release docker images for version $VERSION --" +echo "" +VERSION_SLUG="${VERSION#v}" + +docker build --no-cache "--build-arg=TERRAFORM_VERSION=${VERSION_SLUG}" -t hashicorp/terraform:${VERSION_SLUG} -f "Dockerfile-release" . +docker build --no-cache -t "hashicorp/terraform:${VERSION_SLUG}-full" ../../ diff --git a/scripts/docker-release/hooks/build b/scripts/docker-release/hooks/build deleted file mode 100755 index faed92fb2..000000000 --- a/scripts/docker-release/hooks/build +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# This script assumes that its working directory is the parent directory, -# where the Dockerfile-release file is located, since that's how Dockerhub -# runs hooks. - -set -eu - -# We assume that this is always running while git HEAD is pointed at a release -# tag or a branch that is pointed at the same commit as a release tag. If not, -# this will fail since we can't build a release image for a commit that hasn't -# actually been released. -VERSION="$(git describe)" - -echo "Building release docker images for version $VERSION" -VERSION_SLUG="${VERSION#v}" - -docker build "--build-arg=TERRAFORM_VERSION=${VERSION_SLUG}" -t ${IMAGE_NAME} -f "Dockerfile-release" . diff --git a/scripts/docker-release/push.sh b/scripts/docker-release/push.sh new file mode 100755 index 000000000..e65cd61bc --- /dev/null +++ b/scripts/docker-release/push.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# This script pushes the docker images for the given version of Terraform, +# along with the "light", "full" and "latest" tags, up to docker hub. +# +# You must already be logged in to docker using "docker login" before running +# this script. + +set -eu + +VERSION="$1" +VERSION_SLUG="${VERSION#v}" + +echo "-- Pushing tags $VERSION_SLUG, light, full and latest up to dockerhub --" +echo "" + +docker push "hashicorp/terraform:$VERSION_SLUG" +docker push "hashicorp/terraform:light" +docker push "hashicorp/terraform:full" +docker push "hashicorp/terraform:latest" diff --git a/scripts/docker-release/release.sh b/scripts/docker-release/release.sh new file mode 100755 index 000000000..a297748df --- /dev/null +++ b/scripts/docker-release/release.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash + +# This script is an interactive wrapper around the scripts build.sh, tag.sh +# and push.sh intended for use during official Terraform releases. +# +# This script should be used only when git HEAD is pointing at the release tag +# for what will become the new latest *stable* release, since it will update +# the "latest", "light", and "full" tags to refer to what was built. +# +# To release a specific version without updating the various symbolic tags, +# use build.sh directly and then manually push the single release tag it +# creates. This is appropriate both when publishing a beta version and if, +# for some reason, it's necessary to (re-)publish and older version. + +set -eu + +BASE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd "$BASE" + +# We assume that this is always running while git HEAD is pointed at a release +# tag or a branch that is pointed at the same commit as a release tag. If not, +# this will fail since we can't build a release image for a commit that hasn't +# actually been released. +VERSION="$(git describe)" +VERSION_SLUG="${VERSION#v}" + +# Verify that the version is already deployed to releases.hashicorp.com. +if curl --output /dev/null --silent --head --fail "https://releases.hashicorp.com/terraform/${VERSION_SLUG}/terraform_${VERSION_SLUG}_SHA256SUMS"; then + echo "===== Docker image release for Terraform $VERSION =====" + echo "" +else + cat >&2 <&2 Aborting due to inconsistent version output. + exit 1 +fi +echo "" + +# Update the latest, light and full tags to point to the images we just built. +./tag.sh "$VERSION" + +# Last chance to bail out +echo "-- Prepare to Push --" +echo "" +echo "The following Terraform images are available locally:" +docker images --format "{{.ID}}\t{{.Tag}}" hashicorp/terraform +echo "" +read -p "Ready to push the tags $VERSION_SLUG, light, full, and latest up to dockerhub? " -n 1 -r +echo "" +if ! [[ $REPLY =~ ^[Yy]$ ]]; then + echo >&2 "Aborting because reply wasn't positive." + exit 1 +fi +echo "" + +# Actually upload the images +./push.sh "$VERSION" + +echo "" +echo "-- All done! --" +echo "" +echo "Confirm the release at https://hub.docker.com/r/hashicorp/terraform/tags/" +echo "" diff --git a/scripts/docker-release/tag.sh b/scripts/docker-release/tag.sh new file mode 100755 index 000000000..88bd95f73 --- /dev/null +++ b/scripts/docker-release/tag.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# This script tags the version number given on the command line as being +# the "latest" on the local system only. +# +# The following tags are updated: +# - light (from the tag named after the version number) +# - full (from the tag named after the version number with "-full" appended) +# - latest (as an alias of light) +# +# Before running this the build.sh script must be run to actually create the +# images that this script will tag. +# +# After tagging, use push.sh to push the images to dockerhub. + +set -eu + +VERSION="$1" +VERSION_SLUG="${VERSION#v}" + +echo "-- Updating tags to point to version $VERSION --" +echo "" + +docker tag "hashicorp/terraform:${VERSION_SLUG}" "hashicorp/terraform:light" +docker tag "hashicorp/terraform:${VERSION_SLUG}" "hashicorp/terraform:latest" +docker tag "hashicorp/terraform:${VERSION_SLUG}-full" "hashicorp/terraform:full"