mirror/mirror.sh

188 lines
5.0 KiB
Bash
Raw Permalink Normal View History

2020-12-23 18:39:02 +05:00
#!/usr/bin/env bash
# The Docker Image Mirror script.
2022-06-28 20:15:13 +05:00
# Designed to mirror amd64 and arm64 images somewhere you want.
# Required environment variables:
#
# * REGISTRY: to which registry images should be sent. Should not contain protocols,
# should be the very same as you use 'docker login' with.
# * REGISTRY_USERNAME: user name to use for destination registry.
# * REGISTRY_PASSWORD: password to use for destination registry's user.
# * REGISTRY_PROJECT: path within registry (or it's part) where images will be push'd.
# Should start with '/'!
# What images we will mirror. Collected by executing scripts in "images" directory, see
# "collect_images" function.
2020-12-23 18:39:02 +05:00
WHAT_TO_MIRROR=()
2022-06-28 20:15:13 +05:00
# New tag for currently processing image as it will be used with 'docker push'.
# Updates for every image. Contains only path, image name and base tag from source.
REMOTE_IMAGE=""
# Is image multiarch? E.g. is we're successfully fetched not only amd64 image,
# but also an arm64? Updates for every image.
MULTIARCH=0
function cleanup() {
local image=$1
docker image rm "${image}" &> /dev/null
docker image rm "${REMOTE_IMAGE}" &> /dev/null
docker image rm "${REMOTE_IMAGE}"-amd64 &> /dev/null
docker image rm "${REMOTE_IMAGE}"-arm64 &> /dev/null
}
function collect_images() {
# Load shell files and execute them to get list of mirrorred images.
MIRROR_CONFIGS=$(ls ./images/*.sh)
# shellcheck disable=SC2068
for file in ${MIRROR_CONFIGS[@]}; do
echo "Importing ${file}..."
# shellcheck disable=SC2086,SC2207,SC2206
WHAT_TO_MIRROR=( ${WHAT_TO_MIRROR[@]} $(bash ${file}) )
done
echo "Images to mirror: ${WHAT_TO_MIRROR[*]}"
}
function get_image() {
2022-06-27 12:53:36 +05:00
local image=$1
local arch=$2
2022-06-28 20:15:13 +05:00
2020-12-23 18:39:02 +05:00
image_name=$(echo "${image}" | cut -d":" -f 1)
image_version=$(echo "${image}" | cut -d":" -f 2)
# Check if layers for requested architecture should be fetched.
if docker manifest inspect "${REMOTE_IMAGE}-${arch}" &> /dev/null; then
echo -e "\t* Layers for ${arch} architecture for this image exist at registry we mirror to!"
# This means that no mirrorring for arch-specific image is required.
return 2
fi
echo -ne "\t* Getting ${arch} layers... "
2022-06-28 20:15:13 +05:00
# arm64 layers might be missing. So we just put "FAIL!" here and do nothing else.
2023-01-01 22:44:31 +05:00
if ! docker pull --platform=linux/"${arch}" "${image}" &> "/tmp/pull-${arch}"; then
2022-06-28 20:15:13 +05:00
echo "FAIL!"
2023-01-01 22:44:31 +05:00
echo -e "\nLogs:\n\n" && cat "/tmp/pull-${arch}" && echo -e "\n"
# We presume that amd64 layers are always present. Returning an error here if they're absent.
if [ "${arch}" == "amd64" ]; then
return 1
fi
2022-06-28 20:15:13 +05:00
fi
echo -n "Downloaded, "
2022-06-28 20:16:10 +05:00
# shellcheck disable=SC1083
2022-06-28 20:15:13 +05:00
image_hash=$(docker images -a | grep "^${image_name}" | grep "${image_version}" | awk {' print $3 '})
if ! docker tag "${image_hash}" "${REMOTE_IMAGE}-${arch}" &> /dev/null; then
2022-06-28 20:15:13 +05:00
echo "but tagging failed!"
2022-06-27 00:08:44 +05:00
exit 1
2020-12-23 18:39:02 +05:00
fi
2022-06-28 20:15:13 +05:00
echo "tagged."
2020-12-23 18:39:02 +05:00
# Set multiarch flag for any other arch than amd64.
if [ "${arch}" != "amd64" ]; then
MULTIARCH=1
fi
2020-12-23 18:39:02 +05:00
}
2022-06-28 20:15:13 +05:00
function login_to_registry() {
# Login to registry.
echo "Logging into '${REGISTRY}' as '${REGISTRY_USERNAME}'..."
docker login -u "${REGISTRY_USERNAME}" -p "${REGISTRY_PASSWORD}" "${REGISTRY}"
2022-06-28 20:15:13 +05:00
}
function mirror() {
local image=$1
image_name=$(echo "${image}" | cut -d":" -f 1)
image_version=$(echo "${image}" | cut -d":" -f 2)
echo "* Mirroring ${image}"
2022-06-28 20:15:13 +05:00
REMOTE_IMAGE="${REGISTRY}${REGISTRY_PROJECT}/${image}"
MULTIARCH=0
# We presumes that amd64 image should always be available.
2023-01-01 22:45:16 +05:00
if ! get_image "${image}" "amd64"; then
exitcode=$?
# Shit happened.
if [ ${exitcode} -eq 1 ]; then
echo "! Image mirroring failed! Cannot obtain amd64 image!"
fi
return ${exitcode}
fi
2023-01-01 22:45:16 +05:00
get_image "${image}" "arm64"
2022-06-28 20:15:13 +05:00
push_multiarch_image "${image}"
cleanup "${image}"
}
function push_multiarch_image() {
local image=$1
if ! docker push "${REMOTE_IMAGE}"-amd64 &> /tmp/push-amd64; then
2022-06-28 20:15:13 +05:00
echo -e "\t! amd64 image push failed!"
2023-01-01 22:47:33 +05:00
echo -e "\nLogs:\n\n" && cat /tmp/push-amd64 && echo -e "\n"
else
echo -e "\t* amd64 image pushed"
2022-06-28 20:15:13 +05:00
fi
if ! docker push "${REMOTE_IMAGE}"-arm64 &> /tmp/push-arm64; then
2022-06-28 20:15:13 +05:00
echo -e "\t! arm64 image push failed!"
2023-01-01 22:47:33 +05:00
echo -e "\nLogs:\n\n" && cat /tmp/push-arm64 && echo -e "\n"
else
echo -e "\t* arm64 image pushed"
2022-06-28 20:15:13 +05:00
fi
2022-06-28 20:15:13 +05:00
if [ ${MULTIARCH} -eq 1 ]; then
echo -e "\t* Image is multi-arch, creating and pushing a manifest..."
docker manifest create "${REMOTE_IMAGE}" \
--amend "${REMOTE_IMAGE}"-amd64 \
--amend "${REMOTE_IMAGE}"-arm64 \
&> /dev/null
docker manifest push "${REMOTE_IMAGE}" &> /dev/null
fi
}
function start_docker_daemon() {
# Starting Docker daemon.
/usr/local/bin/dockerd --data-root=/var/lib/docker --max-concurrent-uploads 1 &
2022-06-28 20:15:13 +05:00
# Wait for it.
echo "Waiting for Docker daemon to start..."
while true; do
if docker ps &> /dev/null; then
break
fi
done
}
2022-06-28 20:15:13 +05:00
start_docker_daemon
login_to_registry
collect_images
2022-06-28 20:15:13 +05:00
for package in "${WHAT_TO_MIRROR[@]}"; do
if ! mirror "${package}"; then
exitcode=$?
echo "! Failed to mirror package ${package}! Exit code: ${exitcode} (1 is fatal)."
# Shit happens only for exitcode 1.
if [ ${exitcode} -eq 1 ]; then
exit 1
fi
fi
2020-12-23 18:39:02 +05:00
done