From e5f13f8ab09fe073e4151e3461e9230d959b4781 Mon Sep 17 00:00:00 2001 From: humanoid2050 Date: Sun, 5 Mar 2023 10:56:14 -0500 Subject: [PATCH] testing and debuggin --- .dockerignore | 2 + .gitignore | 1 + Dockerfile | 102 ++++++++++++++++++++++++++++++++++++++++ tools/docker/Dockerfile | 11 ++--- tools/docker/build.sh | 77 ++++++++++++++++-------------- tools/docker/deploy.sh | 72 ++++++++++++++++++++++++++++ tools/docker/lib/common | 44 +++++++++++------ tools/docker/test.sh | 74 +++++++++++++++++++++++++++++ 8 files changed, 329 insertions(+), 54 deletions(-) create mode 100644 Dockerfile create mode 100644 tools/docker/deploy.sh create mode 100755 tools/docker/test.sh diff --git a/.dockerignore b/.dockerignore index 2ce8a8209..0c0f8197c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,3 +9,5 @@ .tox venv docs +/Dockerfile +/docker_cache diff --git a/.gitignore b/.gitignore index 05e3cf47c..c8f67d7f7 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ tags # docker files .docker +/docker_cache # certbot tests .certbot_test_workspace diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..6b91971cb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,102 @@ +#base image +FROM python:3.10-alpine3.16 as certbot + +ENTRYPOINT [ "certbot" ] +EXPOSE 80 443 +VOLUME /etc/letsencrypt /var/lib/letsencrypt +WORKDIR /opt/certbot + +# Copy certbot code +COPY CHANGELOG.md README.rst src/ +COPY tools tools +COPY acme src/acme +COPY certbot src/certbot + +# Install certbot runtime dependencies +RUN apk add --no-cache --virtual .certbot-deps \ + libffi \ + libssl1.1 \ + openssl \ + ca-certificates \ + binutils + +# We set this environment variable and install git while building to try and +# increase the stability of fetching the rust crates needed to build the +# cryptography library +ARG CARGO_NET_GIT_FETCH_WITH_CLI=true +# Install certbot from sources +RUN apk add --no-cache --virtual .build-deps \ + gcc \ + linux-headers \ + openssl-dev \ + musl-dev \ + libffi-dev \ + python3-dev \ + cargo \ + git \ + && python tools/pipstrap.py \ + && python tools/pip_install.py --no-cache-dir \ + --editable src/acme \ + --editable src/certbot \ + && apk del .build-deps \ + && rm -rf ${HOME}/.cargo + +#static definition for making a plugin, but beware that +#using this layer definition will cause collisions if you make +#extensive use of the cache. +FROM certbot as certbot-plugin +COPY --from=plugin-src . /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + +FROM certbot as certbot-dns-dnsmadeeasy +COPY certbot-dns-dnsmadeeasy /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + +FROM certbot as certbot-dns-dnsimple +COPY certbot-dns-dnsimple /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + +FROM certbot as certbot-dns-ovh +COPY certbot-dns-ovh /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + +FROM certbot as certbot-dns-cloudflare +COPY certbot-dns-cloudflare /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + +FROM certbot as certbot-dns-digitalocean +COPY certbot-dns-digitalocean /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + +FROM certbot as certbot-dns-google +COPY certbot-dns-google /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + +FROM certbot as certbot-dns-luadns +COPY certbot-dns-luadns /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + +FROM certbot as certbot-dns-nsone +COPY certbot-dns-nsone /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + +FROM certbot as certbot-dns-rfc2136 +COPY certbot-dns-rfc2136 /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + +FROM certbot as certbot-dns-route53 +COPY certbot-dns-route53 /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + +FROM certbot as certbot-dns-gehirn +COPY certbot-dns-gehirn /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + +FROM certbot as certbot-dns-linode +COPY certbot-dns-linode /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + +FROM certbot as certbot-dns-sakuracloud +COPY certbot-dns-sakuracloud /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + diff --git a/tools/docker/Dockerfile b/tools/docker/Dockerfile index c06b9e5aa..4c9a74bee 100644 --- a/tools/docker/Dockerfile +++ b/tools/docker/Dockerfile @@ -1,7 +1,6 @@ -# Docker Arch (amd64, arm32v6, ...) +#base image FROM python:3.10-alpine3.16 as certbot - ENTRYPOINT [ "certbot" ] EXPOSE 80 443 VOLUME /etc/letsencrypt /var/lib/letsencrypt @@ -42,10 +41,10 @@ RUN apk add --no-cache --virtual .build-deps \ && apk del .build-deps \ && rm -rf ${HOME}/.cargo - +#static definition for making a plugin, but beware that +#using this layer definition will cause collisions if you make +#extensive use of the cache. FROM certbot as certbot-plugin -# Copy Certbot DNS plugin code COPY --from=plugin-src . /opt/certbot/src/plugin - -# Install the DNS plugin RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + diff --git a/tools/docker/build.sh b/tools/docker/build.sh index 783264ff2..5ff81c2f3 100755 --- a/tools/docker/build.sh +++ b/tools/docker/build.sh @@ -1,11 +1,11 @@ #!/bin/bash set -euxo pipefail -IFS=$'\n\t' +# IFS=$'\n\t' # This script builds certbot docker and certbot dns plugins docker using the # local Certbot files. -# Usage: ./build.sh [TAG] [all|amd64|arm32v6|arm64v8] +# Usage: ./build.sh [TAG] [all|] # with the [TAG] value corresponding the base of the tag to give the Docker # images and the 2nd value being the architecture to build snaps for. # Values for the tag should be something like `v0.34.0` or `nightly`. The @@ -16,19 +16,24 @@ WORK_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" REPO_ROOT="$(dirname "$(dirname "${WORK_DIR}")")" source "$WORK_DIR/lib/common" -trap Cleanup EXIT - -Cleanup() { - docker builder rm certbot_builder || true - popd -} - TAG_BASE="$1" if [ -z "$TAG_BASE" ]; then echo "We cannot tag Docker images with an empty string!" >&2 exit 1 fi -ParseRequestedArch "${2}" +PLATFORM_SPEC=$(archList2platformList "${2}") + +#jump to root, matching popd handed by Cleanup on EXIT via trap +pushd "${REPO_ROOT}" + +# Set trap here, as the popd won't work as expected if invoked prior to pushd +trap Cleanup EXIT +Cleanup() { + docker builder rm certbot_builder || true + popd +} + + # just incase the env is not perfectly clean, remove any old instance of the builder docker builder rm certbot_builder || true @@ -37,34 +42,38 @@ docker buildx create --name certbot_builder --driver docker-container --driver-o # add binfmt tools to the docker environment, with integration into the new builder instance docker run --privileged --rm tonistiigi/binfmt --install all -# Step 1: Certbot core Docker -pushd "${REPO_ROOT}" -DOCKER_REPO="${DOCKER_HUB_ORG}/certbot" -for TARGET_ARCH in "${ALL_REQUESTED_ARCH[@]}"; do - docker buildx build \ - --platform $(getPlatform $TARGET_ARCH) \ - --target certbot \ - -f "${WORK_DIR}/Dockerfile" \ - -t "${DOCKER_REPO}:${TARGET_ARCH}-${TAG_BASE}" \ - --load \ - . +#generate the actual Dockerfile with unique layer names +cp ${WORK_DIR}/Dockerfile ${REPO_ROOT}/Dockerfile +for plugin in "${CERTBOT_PLUGINS[@]}"; do + +cat << EOF >> ${REPO_ROOT}/Dockerfile +FROM certbot as certbot-${plugin} +COPY certbot-${plugin} /opt/certbot/src/plugin +RUN python tools/pip_install.py --no-cache-dir --editable /opt/certbot/src/plugin + +EOF done +# Step 1: Certbot core Docker +DOCKER_REPO="${DOCKER_HUB_ORG}/certbot" +docker buildx build \ + --platform ${PLATFORM_SPEC} \ + --target certbot \ + -f "${REPO_ROOT}/Dockerfile" \ + --cache-from=type=local,src=${REPO_ROOT}/docker_cache \ + --cache-to=type=local,dest=${REPO_ROOT}/docker_cache \ + . + # Step 2: Certbot DNS plugins Docker images for plugin in "${CERTBOT_PLUGINS[@]}"; do DOCKER_REPO="${DOCKER_HUB_ORG}/${plugin}" - # Copy QEMU static binaries downloaded when building the core Certbot image - for TARGET_ARCH in "${ALL_REQUESTED_ARCH[@]}"; do - docker buildx build \ - --platform $(getPlatform $TARGET_ARCH) \ - --target certbot-plugin \ - --build-context plugin-src="${REPO_ROOT}/certbot-${plugin}" \ - -f "${WORK_DIR}/Dockerfile" \ - -t "${DOCKER_REPO}:${TARGET_ARCH}-${TAG_BASE}" \ - --load \ - . - done + docker buildx build \ + --platform ${PLATFORM_SPEC} \ + --target certbot-${plugin} \ + --build-context plugin-src="${REPO_ROOT}/certbot-${plugin}" \ + -f "${REPO_ROOT}/Dockerfile" \ + --cache-from=type=local,src=${REPO_ROOT}/docker_cache \ + --cache-to=type=local,dest=${REPO_ROOT}/docker_cache \ + . done - -# popd diff --git a/tools/docker/deploy.sh b/tools/docker/deploy.sh new file mode 100644 index 000000000..4f8f5b7aa --- /dev/null +++ b/tools/docker/deploy.sh @@ -0,0 +1,72 @@ +#!/bin/bash +set -euxo pipefail +# IFS=$'\n\t' + +# This script builds certbot docker and certbot dns plugins docker using the +# local Certbot files. + +# Usage: ./build.sh [TAG] [all|] +# with the [TAG] value corresponding the base of the tag to give the Docker +# images and the 2nd value being the architecture to build snaps for. +# Values for the tag should be something like `v0.34.0` or `nightly`. The +# given value is only the base of the tag because the things like the CPU +# architecture are also added to the full tag. + +WORK_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +REPO_ROOT="$(dirname "$(dirname "${WORK_DIR}")")" +source "$WORK_DIR/lib/common" + +TAG_BASE="$1" +if [ -z "$TAG_BASE" ]; then + echo "We cannot tag Docker images with an empty string!" >&2 + exit 1 +fi +PLATFORM_SPEC=$(archList2platformList "${2}") + +#jump to root, matching popd handed by Cleanup on EXIT via trap +pushd "${REPO_ROOT}" + +# Set trap here, as the popd won't work as expected if invoked prior to pushd +trap Cleanup EXIT +Cleanup() { + docker builder rm certbot_builder || true + popd +} + + + +# just incase the env is not perfectly clean, remove any old instance of the builder +docker builder rm certbot_builder || true +# create the builder instance +docker buildx create --name certbot_builder --driver docker-container --driver-opt=network=host --bootstrap --use +# add binfmt tools to the docker environment, with integration into the new builder instance +docker run --privileged --rm tonistiigi/binfmt --install all + + + +# Step 1: Certbot core Docker +DOCKER_REPO="${DOCKER_HUB_ORG}/certbot" +docker buildx build \ + --platform ${PLATFORM_SPEC} \ + --target certbot \ + -f "${WORK_DIR}/Dockerfile" \ + --cache-from=type=local,src=${REPO_ROOT}/docker_cache \ + --cache-to=type=local,dest=${REPO_ROOT}/docker_cache \ + -t certbot:${TAG_BASE} \ + --push \ + . + +# Step 2: Certbot DNS plugins Docker images +for plugin in "${CERTBOT_PLUGINS[@]}"; do + DOCKER_REPO="${DOCKER_HUB_ORG}/${plugin}" + docker buildx build \ + --platform ${PLATFORM_SPEC} \ + --target certbot-plugin \ + --build-context plugin-src="${REPO_ROOT}/certbot-${plugin}" \ + -f "${WORK_DIR}/Dockerfile" \ + --cache-from=type=local,src=${REPO_ROOT}/docker_cache \ + --cache-to=type=local,dest=${REPO_ROOT}/docker_cache \ + -t certbot-${plugin}:${TAG_BASE} \ + --push \ + . +done diff --git a/tools/docker/lib/common b/tools/docker/lib/common index 9d6cfa9ea..054c253e3 100644 --- a/tools/docker/lib/common +++ b/tools/docker/lib/common @@ -3,8 +3,6 @@ set -ex # Current supported architectures export ALL_TARGET_ARCH=(amd64 arm32v6 arm64v8) -export ALL_DOCKER_PLATFORMS=(linux/amd64 linux/arm/v6 linux/arm64) - # Name of the Certbot Docker organizaation on GitHub. After creating # repositories with the same names (e.g. "certbot", "dns-dnsmadeeasy", etc.) @@ -30,7 +28,11 @@ export CERTBOT_PLUGINS=( "dns-sakuracloud" ) -getPlatform() { +# Converts input architecture identifier to the platform specification +# understood by `docker build buildx --platform `. +# Usage: arch2platform [arm64|arm32v6|arm64v8] +# If the input is not recognized, an error is returned +arch2platform() { REQUESTED_ARCH="${1}" case $REQUESTED_ARCH in amd64) @@ -42,25 +44,39 @@ getPlatform() { arm64v8) echo "linux/arm64" ;; + *) + return 1 + ;; esac } # Parses the requested architecture string and sets ALL_REQUESTED_ARCH to # result. -# Usage: ParseRequestedArch [all|amd64|arm32v6|arm64v8] -ParseRequestedArch() { +# Usage: archList2platformList all +# archList2platformList [arch-list] +# where [arch-list] is a comma separated list of architectures +# as interpreted by the arch2platform function +archList2platformList() { + local IFS="," REQUESTED_ARCH="${1}" + # Handle the special value "all" if [[ "${REQUESTED_ARCH}" == "all" ]]; then - ALL_REQUESTED_ARCH=("${ALL_TARGET_ARCH[@]}") + # Recursive call using the list of all known architectures cast to + # comma separated list + archList2platformList "${ALL_TARGET_ARCH[*]}" return 0 fi - for TARGET_ARCH in "${ALL_TARGET_ARCH[@]}"; do - if [[ "${TARGET_ARCH}" == "${REQUESTED_ARCH}" ]]; then - ALL_REQUESTED_ARCH=("${REQUESTED_ARCH}") - return 0 - fi + # Convert comma separated list to array of strings + read -ra REQUESTED_ARCH_LIST <<< "$REQUESTED_ARCH" + # Convert each string to the corresponding docker platform specification. + # The internal call to arch2platform might return an error if the arch is + # not recognized, crashing the process (`set -ex` called at beginning of + # script) + PLATFORM_LIST=() + for TARGET_ARCH in "${REQUESTED_ARCH_LIST[@]}"; do + PLATFORM_LIST+=($(arch2platform "$TARGET_ARCH")) done - # If we didn't return above, REQUESTED_ARCH has an unexpected value. - echo "Unexpected target architecture \"${REQUESTED_ARCH}\"". >&2 - exit 1 + + # Return a string made from the array of docker platform spedifications + echo "${PLATFORM_LIST[*]}" } diff --git a/tools/docker/test.sh b/tools/docker/test.sh new file mode 100755 index 000000000..48a6db26d --- /dev/null +++ b/tools/docker/test.sh @@ -0,0 +1,74 @@ +#!/bin/bash +set -euxo pipefail + +WORK_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +REPO_ROOT="$(dirname "$(dirname "${WORK_DIR}")")" +source "$WORK_DIR/lib/common" + + +TAG_BASE="$1" +if [ -z "$TAG_BASE" ]; then + echo "We cannot tag Docker images with an empty string!" >&2 + exit 1 +fi +PLATFORM_SPEC=$(archList2platformList "${2}") + +#jump to root, matching popd handed by Cleanup on EXIT via trap +pushd "${REPO_ROOT}" + +# Set trap here, as the popd won't work as expected if invoked prior to pushd +trap Cleanup EXIT +Cleanup() { + docker builder rm certbot_builder || true + popd +} + + + +# just incase the env is not perfectly clean, remove any old instance of the builder +docker builder rm certbot_builder || true +# create the builder instance +docker buildx create --name certbot_builder --driver docker-container --driver-opt=network=host --bootstrap --use +# add binfmt tools to the docker environment, with integration into the new builder instance +docker run --privileged --rm tonistiigi/binfmt --install all + + + + +# Step 1: Certbot core Docker +DOCKER_REPO="${DOCKER_HUB_ORG}/certbot" +IFS_OLD="$IFS" +IFS="," +read -ra REQUESTED_ARCH_LIST <<< "$2" +IFS="$IFS_OLD" +for ARCH in "${REQUESTED_ARCH_LIST[@]}"; do + + docker buildx build \ + --platform $(arch2platform ${ARCH}) \ + --target certbot \ + -f "${WORK_DIR}/Dockerfile" \ + --cache-from=type=local,src=${REPO_ROOT}/docker_cache \ + --cache-to=type=local,dest=${REPO_ROOT}/docker_cache \ + -t certbot:${ARCH}-${TAG_BASE} \ + --load \ + . + docker run --rm "certbot:${ARCH}-${TAG_BASE}" plugins --prepare + + # Step 2: Certbot DNS plugins Docker images + for plugin in "${CERTBOT_PLUGINS[@]}"; do + DOCKER_REPO="${DOCKER_HUB_ORG}/${plugin}" + docker buildx build \ + --platform $(arch2platform ${ARCH}) \ + --target certbot-plugin \ + --build-context plugin-src="${REPO_ROOT}/certbot-${plugin}" \ + -f "${WORK_DIR}/Dockerfile" \ + --cache-from=type=local,src=${REPO_ROOT}/docker_cache \ + --cache-to=type=local,dest=${REPO_ROOT}/docker_cache \ + -t certbot-${plugin}:${ARCH}-${TAG_BASE} \ + --load \ + . + docker run --rm "certbot-${plugin}:${ARCH}-${TAG_BASE}" plugins --prepare + + done + +done