netbox-docker/build.sh

373 lines
14 KiB
Bash
Executable File

#!/bin/bash
# Clones the Netbox repository with git from Github and builds the Dockerfile
echo "▶️ $0 $*"
set -e
if [ "${1}x" == "x" ] || [ "${1}" == "--help" ] || [ "${1}" == "-h" ]; then
echo "Usage: ${0} <branch> [--push|--push-only]"
echo " branch The branch or tag to build. Required."
echo " --push Pushes the built Docker image to the registry."
echo " --push-only Only pushes the Docker image to the registry, but does not build it."
echo ""
echo "You can use the following ENV variables to customize the build:"
echo " SRC_ORG Which fork of netbox to use (i.e. github.com/\${SRC_ORG}/\${SRC_REPO})."
echo " Default: netbox-community"
echo " SRC_REPO The name of the repository to use (i.e. github.com/\${SRC_ORG}/\${SRC_REPO})."
echo " Default: netbox"
echo " URL Where to fetch the code from."
echo " Must be a git repository. Can be private."
echo " Default: https://github.com/\${SRC_ORG}/\${SRC_REPO}.git"
echo " NETBOX_PATH The path where netbox will be checkout out."
echo " Must not be outside of the netbox-docker repository (because of Docker)!"
echo " Default: .netbox"
echo " SKIP_GIT If defined, git is not invoked and \${NETBOX_PATH} will not be altered."
echo " This may be useful, if you are manually managing the NETBOX_PATH."
echo " Default: undefined"
echo " TAG The version part of the docker tag."
echo " Default:"
echo " When <branch>=master: latest"
echo " When <branch>=develop: snapshot"
echo " Else: same as <branch>"
echo " DOCKER_REGISTRY The Docker repository's registry (i.e. '\${DOCKER_REGISTRY}/\${DOCKER_ORG}/\${DOCKER_REPO}'')"
echo " Used for tagging the image."
echo " Default: docker.io"
echo " DOCKER_ORG The Docker repository's organisation (i.e. '\${DOCKER_REGISTRY}/\${DOCKER_ORG}/\${DOCKER_REPO}'')"
echo " Used for tagging the image."
echo " Default: netboxcommunity"
echo " DOCKER_REPO The Docker repository's name (i.e. '\${DOCKER_REGISTRY}/\${DOCKER_ORG}/\${DOCKER_REPO}'')"
echo " Used for tagging the image."
echo " Default: netbox"
echo " DOCKER_TAG The name of the tag which is applied to the image."
echo " Useful for pushing into another registry than hub.docker.com."
echo " Default: \${DOCKER_REGISTRY}/\${DOCKER_ORG}/\${DOCKER_REPO}:\${TAG}"
echo " DOCKER_SHORT_TAG The name of the short tag which is applied to the"
echo " image. This is used to tag all patch releases to their"
echo " containing version e.g. v2.5.1 -> v2.5"
echo " Default: \${DOCKER_REGISTRY}/\${DOCKER_ORG}/\${DOCKER_REPO}:<MAJOR>.<MINOR>"
echo " DOCKERFILE The name of Dockerfile to use."
echo " Default: Dockerfile"
echo " DOCKER_FROM The base image to use."
echo " Default: 'python:3.8-alpine'"
echo " DOCKER_TARGET A specific target to build."
echo " It's currently not possible to pass multiple targets."
echo " Default: main ldap"
echo " HTTP_PROXY The proxy to use for http requests."
echo " Example: http://proxy.domain.tld:3128"
echo " Default: undefined"
echo " NO_PROXY Comma-separated list of domain extensions proxy should not be used for."
echo " Example: .domain1.tld,.domain2.tld"
echo " Default: undefined"
echo " DEBUG If defined, the script does not stop when certain checks are unsatisfied."
echo " Default: undefined"
echo " DRY_RUN Prints all build statements instead of running them."
echo " Default: undefined"
echo " GH_ACTION If defined, special 'echo' statements are enabled that set the"
echo " following environment variables in Github Actions:"
echo " - FINAL_DOCKER_TAG: The final value of the DOCKER_TAG env variable"
echo " Default: undefined"
echo ""
echo "Examples:"
echo " ${0} master"
echo " This will fetch the latest 'master' branch, build a Docker Image and tag it"
echo " 'netboxcommunity/netbox:latest'."
echo " ${0} develop"
echo " This will fetch the latest 'develop' branch, build a Docker Image and tag it"
echo " 'netboxcommunity/netbox:snapshot'."
echo " ${0} v2.6.6"
echo " This will fetch the 'v2.6.6' tag, build a Docker Image and tag it"
echo " 'netboxcommunity/netbox:v2.6.6' and 'netboxcommunity/netbox:v2.6'."
echo " ${0} develop-2.7"
echo " This will fetch the 'develop-2.7' branch, build a Docker Image and tag it"
echo " 'netboxcommunity/netbox:develop-2.7'."
echo " SRC_ORG=cimnine ${0} feature-x"
echo " This will fetch the 'feature-x' branch from https://github.com/cimnine/netbox.git,"
echo " build a Docker Image and tag it 'netboxcommunity/netbox:feature-x'."
echo " SRC_ORG=cimnine DOCKER_ORG=cimnine ${0} feature-x"
echo " This will fetch the 'feature-x' branch from https://github.com/cimnine/netbox.git,"
echo " build a Docker Image and tag it 'cimnine/netbox:feature-x'."
if [ "${1}x" == "x" ]; then
exit 1
else
exit 0
fi
fi
###
# Enabling dry-run mode
###
if [ -z "${DRY_RUN}" ]; then
DRY=""
else
echo "⚠️ DRY_RUN MODE ON ⚠️"
DRY="echo"
fi
###
# Variables for fetching the source
###
SRC_ORG="${SRC_ORG-netbox-community}"
SRC_REPO="${SRC_REPO-netbox}"
NETBOX_BRANCH="${1}"
URL="${URL-https://github.com/${SRC_ORG}/${SRC_REPO}.git}"
NETBOX_PATH="${NETBOX_PATH-.netbox}"
###
# Fetching the source
###
if [ "${2}" != "--push-only" ] && [ -z "${SKIP_GIT}" ] ; then
echo "🌐 Checking out '${NETBOX_BRANCH}' of netbox from the url '${URL}' into '${NETBOX_PATH}'"
if [ ! -d "${NETBOX_PATH}" ]; then
$DRY git clone -q --depth 10 -b "${NETBOX_BRANCH}" "${URL}" "${NETBOX_PATH}"
fi
(
$DRY cd "${NETBOX_PATH}"
if [ -n "${HTTP_PROXY}" ]; then
git config http.proxy "${HTTP_PROXY}"
fi
$DRY git remote set-url origin "${URL}"
$DRY git fetch -qp --depth 10 origin "${NETBOX_BRANCH}"
$DRY git checkout -qf FETCH_HEAD
$DRY git prune
)
echo "✅ Checked out netbox"
fi
###
# Determining the value for DOCKERFILE
# and checking whether it exists
###
DOCKERFILE="${DOCKERFILE-Dockerfile}"
if [ ! -f "${DOCKERFILE}" ]; then
echo "🚨 The Dockerfile ${DOCKERFILE} doesn't exist."
if [ -z "${DEBUG}" ]; then
exit 1
else
echo "⚠️ Would exit here with code '1', but DEBUG is enabled."
fi
fi
###
# Determining the value for DOCKER_FROM
###
if [ -z "$DOCKER_FROM" ]; then
DOCKER_FROM="python:3.8-alpine"
fi
###
# Variables for labelling the docker image
###
BUILD_DATE="$(date -u '+%Y-%m-%dT%H:%M+00:00')"
if [ -d ".git" ]; then
GIT_REF="$(git rev-parse HEAD)"
fi
# Read the project version from the `VERSION` file and trim it, see https://stackoverflow.com/a/3232433/172132
PROJECT_VERSION="${PROJECT_VERSION-$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' VERSION)}"
# Get the Git information from the netbox directory
if [ -d "${NETBOX_PATH}/.git" ]; then
NETBOX_GIT_REF=$(cd "${NETBOX_PATH}"; git rev-parse HEAD)
NETBOX_GIT_BRANCH=$(cd "${NETBOX_PATH}"; git rev-parse --abbrev-ref HEAD)
NETBOX_GIT_URL=$(cd "${NETBOX_PATH}"; git remote get-url origin)
fi
###
# Variables for tagging the docker image
###
DOCKER_REGISTRY="${DOCKER_REGISTRY-docker.io}"
DOCKER_ORG="${DOCKER_ORG-netboxcommunity}"
DOCKER_REPO="${DOCKER_REPO-netbox}"
case "${NETBOX_BRANCH}" in
master)
TAG="${TAG-latest}";;
develop)
TAG="${TAG-snapshot}";;
*)
TAG="${TAG-$NETBOX_BRANCH}";;
esac
###
# Determine targets to build
###
DEFAULT_DOCKER_TARGETS=("main" "ldap")
DOCKER_TARGETS=( "${DOCKER_TARGET:-"${DEFAULT_DOCKER_TARGETS[@]}"}")
echo "🏭 Building the following targets:" "${DOCKER_TARGETS[@]}"
###
# Build each target
###
export DOCKER_BUILDKIT=${DOCKER_BUILDKIT-1}
for DOCKER_TARGET in "${DOCKER_TARGETS[@]}"; do
echo "🏗 Building the target '${DOCKER_TARGET}'"
###
# composing the final TARGET_DOCKER_TAG
###
TARGET_DOCKER_TAG="${DOCKER_TAG-${DOCKER_REGISTRY}/${DOCKER_ORG}/${DOCKER_REPO}:${TAG}}"
if [ "${DOCKER_TARGET}" != "main" ]; then
TARGET_DOCKER_TAG="${TARGET_DOCKER_TAG}-${DOCKER_TARGET}"
fi
if [ -n "${GH_ACTION}" ]; then
echo "::set-env name=FINAL_DOCKER_TAG::${TARGET_DOCKER_TAG}"
echo "::set-output name=skipped::false"
fi
###
# composing the additional DOCKER_SHORT_TAG,
# i.e. "v2.6.1" becomes "v2.6",
# which is only relevant for version tags
# Also let "latest" follow the highest version
###
if [[ "${TAG}" =~ ^v([0-9]+)\.([0-9]+)\.[0-9]+$ ]]; then
MAJOR=${BASH_REMATCH[1]}
MINOR=${BASH_REMATCH[2]}
TARGET_DOCKER_SHORT_TAG="${DOCKER_SHORT_TAG-${DOCKER_REGISTRY}/${DOCKER_ORG}/${DOCKER_REPO}:v${MAJOR}.${MINOR}}"
TARGET_DOCKER_LATEST_TAG="${DOCKER_REGISTRY}/${DOCKER_ORG}/${DOCKER_REPO}:latest"
if [ "${DOCKER_TARGET}" != "main" ]; then
TARGET_DOCKER_SHORT_TAG="${TARGET_DOCKER_SHORT_TAG}-${DOCKER_TARGET}"
TARGET_DOCKER_LATEST_TAG="${TARGET_DOCKER_LATEST_TAG}-${DOCKER_TARGET}"
fi
fi
###
# Proceeding to buils stage, except if `--push-only` is passed
###
if [ "${2}" != "--push-only" ] ; then
###
# Checking if the build is necessary,
# meaning build only if one of those values changed:
# - Python base image digest (Label: PYTHON_BASE_DIGEST)
# - netbox git ref (Label: NETBOX_GIT_REF)
# - netbox-docker git ref (Label: org.label-schema.vcs-ref)
###
# Load information from registry (only for docker.io)
SHOULD_BUILD="false"
BUILD_REASON=""
if [ -z "${GH_ACTION}" ]; then
# Asuming non Github builds should always proceed
SHOULD_BUILD="true"
BUILD_REASON="${BUILD_REASON} interactive"
elif [ "$DOCKER_REGISTRY" = "docker.io" ]; then
source ./build-functions/get-public-image-config.sh
IFS=':' read -ra DOCKER_FROM_SPLIT <<< "${DOCKER_FROM}"
if ! [[ ${DOCKER_FROM_SPLIT[0]} =~ .*/.* ]]; then
# Need to use "library/..." for images the have no two part name
DOCKER_FROM_SPLIT[0]="library/${DOCKER_FROM_SPLIT[0]}"
fi
PYTHON_LAST_LAYER=$(get_image_last_layer "${DOCKER_FROM_SPLIT[0]}" "${DOCKER_FROM_SPLIT[1]}")
mapfile -t IMAGES_LAYERS_OLD < <(get_image_layers "${DOCKER_ORG}"/"${DOCKER_REPO}" "${TAG}")
NETBOX_GIT_REF_OLD=$(get_image_label NETBOX_GIT_REF "${DOCKER_ORG}"/"${DOCKER_REPO}" "${TAG}")
GIT_REF_OLD=$(get_image_label org.label-schema.vcs-ref "${DOCKER_ORG}"/"${DOCKER_REPO}" "${TAG}")
if ! printf '%s\n' "${IMAGES_LAYERS_OLD[@]}" | grep -q -P "^${PYTHON_LAST_LAYER}\$"; then
SHOULD_BUILD="true"
BUILD_REASON="${BUILD_REASON} python"
fi
if [ "${NETBOX_GIT_REF}" != "${NETBOX_GIT_REF_OLD}" ]; then
SHOULD_BUILD="true"
BUILD_REASON="${BUILD_REASON} netbox"
fi
if [ "${GIT_REF}" != "${GIT_REF_OLD}" ]; then
SHOULD_BUILD="true"
BUILD_REASON="${BUILD_REASON} netbox-docker"
fi
else
SHOULD_BUILD="true"
BUILD_REASON="${BUILD_REASON} no-check"
fi
###
# Composing all arguments for `docker build`
###
DOCKER_BUILD_ARGS=(
--pull
--target "${DOCKER_TARGET}"
-f "${DOCKERFILE}"
-t "${TARGET_DOCKER_TAG}"
)
if [ -n "${TARGET_DOCKER_SHORT_TAG}" ]; then
DOCKER_BUILD_ARGS+=( -t "${TARGET_DOCKER_SHORT_TAG}" )
DOCKER_BUILD_ARGS+=( -t "${TARGET_DOCKER_LATEST_TAG}" )
fi
# --label
DOCKER_BUILD_ARGS+=(
--label "ORIGINAL_TAG=${TARGET_DOCKER_TAG}"
--label "org.label-schema.build-date=${BUILD_DATE}"
--label "org.opencontainers.image.created=${BUILD_DATE}"
--label "org.label-schema.version=${PROJECT_VERSION}"
--label "org.opencontainers.image.version=${PROJECT_VERSION}"
)
if [ -d ".git" ]; then
DOCKER_BUILD_ARGS+=(
--label "org.label-schema.vcs-ref=${GIT_REF}"
--label "org.opencontainers.image.revision=${GIT_REF}"
)
fi
if [ -d "${NETBOX_PATH}/.git" ]; then
DOCKER_BUILD_ARGS+=(
--label "NETBOX_GIT_BRANCH=${NETBOX_GIT_BRANCH}"
--label "NETBOX_GIT_REF=${NETBOX_GIT_REF}"
--label "NETBOX_GIT_URL=${NETBOX_GIT_URL}"
)
fi
if [ -n "${BUILD_REASON}" ]; then
BUILD_REASON=$(sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' <<< "$BUILD_REASON")
DOCKER_BUILD_ARGS+=( --label "BUILD_REASON=${BUILD_REASON}" )
fi
# --build-arg
DOCKER_BUILD_ARGS+=( --build-arg "NETBOX_PATH=${NETBOX_PATH}" )
if [ -n "${DOCKER_FROM}" ]; then
DOCKER_BUILD_ARGS+=( --build-arg "FROM=${DOCKER_FROM}" )
fi
if [ -n "${HTTP_PROXY}" ]; then
DOCKER_BUILD_ARGS+=( --build-arg "http_proxy=${HTTP_PROXY}" )
DOCKER_BUILD_ARGS+=( --build-arg "https_proxy=${HTTPS_PROXY}" )
fi
if [ -n "${NO_PROXY}" ]; then
DOCKER_BUILD_ARGS+=( --build-arg "no_proxy=${NO_PROXY}" )
fi
###
# Building the docker image
###
if [ "${SHOULD_BUILD}" == "true" ]; then
echo "🐳 Building the Docker image '${TARGET_DOCKER_TAG}'."
echo " Build reason set to: ${BUILD_REASON}"
$DRY docker build "${DOCKER_BUILD_ARGS[@]}" .
echo "✅ Finished building the Docker images '${TARGET_DOCKER_TAG}'"
echo "🔎 Inspecting labels on '${TARGET_DOCKER_TAG}'"
$DRY docker inspect "${TARGET_DOCKER_TAG}" --format "{{json .Config.Labels}}"
else
echo "Build skipped because sources didn't change"
echo "::set-output name=skipped::true"
fi
fi
###
# Pushing the docker images if either `--push` or `--push-only` are passed
###
if [ "${2}" == "--push" ] || [ "${2}" == "--push-only" ] ; then
source ./build-functions/docker-functions.sh
push_image_to_registry "${TARGET_DOCKER_TAG}"
if [ -n "${TARGET_DOCKER_SHORT_TAG}" ]; then
push_image_to_registry "${TARGET_DOCKER_SHORT_TAG}"
push_image_to_registry "${TARGET_DOCKER_LATEST_TAG}"
fi
fi
done