summaryrefslogtreecommitdiffstats
path: root/docker/scripts/update_dockerhub_prod_tags.sh
diff options
context:
space:
mode:
Diffstat (limited to 'docker/scripts/update_dockerhub_prod_tags.sh')
-rwxr-xr-xdocker/scripts/update_dockerhub_prod_tags.sh402
1 files changed, 402 insertions, 0 deletions
diff --git a/docker/scripts/update_dockerhub_prod_tags.sh b/docker/scripts/update_dockerhub_prod_tags.sh
new file mode 100755
index 00000000..01dac644
--- /dev/null
+++ b/docker/scripts/update_dockerhub_prod_tags.sh
@@ -0,0 +1,402 @@
+#! /bin/bash
+
+# Copyright (c) 2022 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -euo pipefail
+shopt -s extglob
+
+# Log all output to stdout & stderr to a log file
+logname="/tmp/$(basename $0).$(date -u +%Y_%m_%d_%H%M%S).log"
+echo -e "\n*** Logging output to $logname ***\n"
+exec > >(tee -a $logname) 2>&1
+
+export CIMAN_DOCKER_SCRIPTS=${CIMAN_DOCKER_SCRIPTS:-"$(dirname $BASH_SOURCE)"}
+. "$CIMAN_DOCKER_SCRIPTS/lib_common.sh"
+
+# Global variables
+long_bar="################################################################"
+short_bar="-----"
+image_not_found=""
+image_user=""
+image_repo=""
+image_version=""
+image_arch=""
+image_name_prod=""
+image_name_prev=""
+image_name_new=""
+image_realname=""
+image_realname_prod=""
+image_realname_prev=""
+image_tags=""
+image_tags_prod=""
+image_tags_prev=""
+image_tags_new=""
+docker_id_prod=""
+docker_id_prev=""
+docker_id_new=""
+digest_prod=""
+digest_prev=""
+digest_new=""
+restore_cmd=""
+
+usage() {
+ local script="$(basename $0)"
+ echo
+ echo "Usage: $script r[evert] <prod image>"
+ echo " $script p[romote] <new image> [<new image>]"
+ echo " $script i[nspect] <prod image>"
+ echo
+ echo " revert: swaps 'prod-<arch>' and 'prod-prev-<arch>' images"
+ echo " <prod image>: e.g. fdiotools/builder-ubuntu2204:prod-x86_64"
+ echo
+ echo " promote: moves 'prod-<arch>' image to 'prod-prev-<arch>' tag and"
+ echo " tags <new image> with 'prod-<arch>'"
+ echo " <new image>: e.g. fdiotools/builder-ubuntu2204:2022_07_23_151655-x86_64"
+ echo " inspect: prints out all tags for prod-<arch> and prod-prev-<arch>"
+ echo
+ exit 1
+}
+
+echo_restore_cmd() {
+ echo -e "\n$long_bar\n"
+ echo "To restore tags to original state, issue the following command:"
+ echo -e "\n$restore_cmd\n\n$long_bar\n"
+}
+
+push_to_dockerhub() {
+ echo_restore_cmd
+ for image in "$@" ; do
+ set +e
+ echo "Pushing '$image' to docker hub..."
+ if ! docker push "$image" ; then
+ echo "ERROR: 'docker push $image' failed!"
+ exit 1
+ fi
+ done
+}
+
+parse_image_name() {
+ image_user="$(echo $1 | cut -d'/' -f1)"
+ image_repo="$(echo $1 | cut -d'/' -f2 | cut -d':' -f1)"
+ local tag="$(echo $1 | cut -d':' -f2)"
+ image_version="$(echo $tag | cut -d'-' -f1)"
+ image_arch="$(echo $tag | sed -e s/$image_version-//)"
+ image_name_new="${image_user}/${image_repo}:${image_version}-${image_arch}"
+ if [ "$1" != "$image_name_new" ] ; then
+ echo "ERROR: Image name parsing failed: $1 != '$image_name_new'"
+ usage
+ fi
+ if [[ "$image_version" =~ "prod" ]] ; then
+ image_name_new=""
+ fi
+ image_name_prod="${image_user}/${image_repo}:prod-${image_arch}"
+ image_name_prev="${image_user}/${image_repo}:prod-prev-${image_arch}"
+}
+
+format_image_tags() {
+ # Note: 'grep $image_arch' & grep -v 'prod-curr' is required due to a
+ # bug in docker hub which returns old tags which were deleted via
+ # the webUI, but are still retrieved by 'docker pull -a'
+ image_tags="$(docker images | grep $1 | grep $image_arch | grep -v prod-curr | sort -r | mawk '{print $1":"$2}' | tr '\n' ' ')"
+ image_realname="$(docker images | grep $1 | grep $image_arch | sort -r | grep -v prod | mawk '{print $1":"$2}' || true)"
+ if [ -z "${image_realname:-}" ] ; then
+ image_realname="$image_tags"
+ fi
+}
+
+get_image_id_tags() {
+ for image in "$image_name_new" "$image_name_prod" "$image_name_prev" ; do
+ if [ -z "$image" ] ; then
+ continue
+ fi
+ # ensure image exists
+ set +e
+ local image_found="$(docker images | mawk '{print $1":"$2}' | grep $image)"
+ set -e
+ if [ -z "$image_found" ] ; then
+ if [ "$image" = "$image_name_prev" ] ; then
+ if [ "$action" = "revert" ] ; then
+ echo "ERROR: Image '$image' not found!"
+ echo "Unable to revert production image '$image_name_prod'!"
+ usage
+ else
+ continue
+ fi
+ else
+ echo "ERROR: Image '$image' not found!"
+ usage
+ fi
+ fi
+ set +e
+ local id="$(docker image inspect $image | mawk -F':' '/Id/{print $3}')"
+ local digest="$(docker image inspect $image | grep -A1 RepoDigests | grep -v RepoDigests | mawk -F':' '{print $2}')"
+ local retval="$?"
+ set -e
+ if [ "$retval" -ne "0" ] ; then
+ echo "ERROR: Docker ID not found for '$image'!"
+ usage
+ fi
+ if [ "$image" = "$image_name_prod" ] ; then
+ docker_id_prod="${id::12}"
+ digest_prod="${digest::12}"
+ format_image_tags "$docker_id_prod"
+ image_tags_prod="$image_tags"
+ if [ -z "$image_realname_prod" ] ; then
+ image_realname_prod="$image_realname"
+ fi
+ elif [ "$image" = "$image_name_prev" ] ; then
+ docker_id_prev="${id::12}"
+ digest_prev="${digest::12}"
+ format_image_tags "$docker_id_prev"
+ image_tags_prev="$image_tags"
+ if [ -z "$image_realname_prev" ] ; then
+ image_realname_prev="$image_realname"
+ fi
+ else
+ docker_id_new="${id::12}"
+ digest_new="${digest::12}"
+ format_image_tags "$docker_id_new" "NEW"
+ image_tags_new="$image_tags"
+ fi
+ done
+ if [ -z "$restore_cmd" ] ; then
+ restore_cmd="sudo $0 p $image_realname_prev $image_realname_prod"
+ fi
+}
+
+get_all_tags_from_dockerhub() {
+ local dh_repo="$image_user/$image_repo"
+ echo -e "Pulling all tags from docker hub repo '$dh_repo':\n$long_bar"
+ if ! docker pull -a "$dh_repo" ; then
+ echo "ERROR: Repository '$dh_repo' not found on docker hub!"
+ usage
+ fi
+ echo "$long_bar"
+}
+
+verify_image_version_date_format() {
+ version="$1"
+ local ver_regex="^[0-9]{4}_[0-1][0-9]_[0-3][0-9]_[0-2][0-9][0-5][0-9][0-5][0-9]_UTC$"
+ if [[ "$version" =~ $ver_regex ]]; then
+ return 0
+ fi
+ return 1
+}
+
+verify_image_name() {
+ image_not_found=""
+ # Invalid user
+ if [ "$image_user" != "fdiotools" ] ; then
+ image_not_found="true"
+ echo "ERROR: invalid user '$image_user' in '$image_name_new'!"
+ fi
+ # Invalid version
+ if [ -z "$image_not_found" ] \
+ && [ "$image_version" != "prod" ] \
+ && ! verify_image_version_date_format "$image_version" ]] ; then
+ image_not_found="true"
+ echo "ERROR: invalid version '$image_version' in '$image_name_new'!"
+ fi
+ # Invalid arch
+ if [ -z "$image_not_found" ] \
+ && ! [[ "$EXECUTOR_ARCHS" =~ .*"$image_arch".* ]] ; then
+ image_not_found="true"
+ echo "ERROR: invalid arch '$image_arch' in '$image_name_new'!"
+ fi
+ if [ -n "$image_not_found" ] ; then
+ echo "ERROR: Invalid image '$image_name_new'!"
+ usage
+ fi
+}
+
+docker_tag_image() {
+ echo ">>> docker tag $1 $2"
+ set +e
+ docker tag "$1" "$2"
+ local retval="$?"
+ set -e
+ if [ "$retval" -ne "0" ] ; then
+ echo "WARNING: 'docker tag $1 $2' failed!"
+ fi
+}
+
+docker_rmi_tag() {
+ set +e
+ echo ">>> docker rmi $1"
+ docker rmi "$1"
+ local retval="$?"
+ set -e
+ if [ "$retval" -ne "0" ] ; then
+ echo "WARNING: 'docker rmi $1' failed!"
+ fi
+}
+
+print_image_list() {
+ if [ -z "$2" ] ; then
+ echo "$1 Image Not Found"
+ return
+ fi
+ echo "$1 (Id $2, Digest $3):"
+ for image in $4 ; do
+ echo -e "\t$image"
+ done
+}
+
+inspect_images() {
+ echo -e "\n${1}Production Docker Images:"
+ echo "$short_bar"
+ if [ -n "$image_tags_new" ] ; then
+ print_image_list "NEW" "$docker_id_new" "$digest_new" "$image_tags_new"
+ echo
+ fi
+ print_image_list "prod-$image_arch" "$docker_id_prod" "$digest_prod" \
+ "$image_tags_prod"
+ echo
+ print_image_list "prod-prev-$image_arch" "$docker_id_prev" "$digest_prev" \
+ "$image_tags_prev"
+ echo -e "$short_bar\n"
+}
+
+revert_prod_image() {
+ inspect_images "EXISTING "
+ docker_tag_image "$docker_id_prod" "$image_name_prev"
+ docker_tag_image "$docker_id_prev" "$image_name_prod"
+ get_image_id_tags
+ inspect_images "REVERTED "
+
+ local yn=""
+ while true; do
+ read -p "Push Reverted tags to '$image_user/$image_repo' (yes/no)? " yn
+ case ${yn:0:1} in
+ y|Y )
+ break ;;
+ n|N )
+ echo -e "\nABORTING REVERT!\n"
+ docker_tag_image $docker_id_prev $image_name_prod
+ docker_tag_image $docker_id_prod $image_name_prev
+ get_image_id_tags
+ inspect_images "RESTORED LOCAL "
+ exit 1 ;;
+ * )
+ echo "Please answer yes or no." ;;
+ esac
+ done
+ echo
+ push_to_dockerhub $image_name_prev $image_name_prod
+ inspect_images ""
+ echo_restore_cmd
+}
+
+promote_new_image() {
+ inspect_images "EXISTING "
+ docker_tag_image "$docker_id_prod" "$image_name_prev"
+ docker_tag_image "$docker_id_new" "$image_name_prod"
+ get_image_id_tags
+ inspect_images "PROMOTED "
+
+ local yn=""
+ while true; do
+ read -p "Push promoted tags to '$image_user/$image_repo' (yes/no)? " yn
+ case "${yn:0:1}" in
+ y|Y )
+ break ;;
+ n|N )
+ echo -e "\nABORTING PROMOTION!\n"
+ docker_tag_image "$docker_id_prev" "$image_name_prod"
+ local restore_both="$(echo $restore_cmd | mawk '{print $5}')"
+ if [[ -n "$restore_both" ]] ; then
+ docker_tag_image "$image_realname_prev" "$image_name_prev"
+ else
+ docker_rmi_tag "$image_name_prev"
+ image_name_prev=""
+ docker_id_prev=""
+ fi
+ get_image_id_tags
+ inspect_images "RESTORED "
+ exit 1 ;;
+ * )
+ echo "Please answer yes or no." ;;
+ esac
+ done
+ echo
+ push_to_dockerhub "$image_name_new" "$image_name_prev" "$image_name_prod"
+ inspect_images ""
+ echo_restore_cmd
+}
+
+must_be_run_as_root_or_docker_group
+
+# Validate arguments
+num_args="$#"
+if [ "$num_args" -lt "1" ] ; then
+ usage
+fi
+action=""
+case "$1" in
+ r?(evert))
+ action="revert"
+ if [ "$num_args" -ne "2" ] ; then
+ echo "ERROR: Invalid number of arguments: $#"
+ usage
+ fi ;;
+ p?(romote))
+ if [ "$num_args" -eq "2" ] || [ "$num_args" -eq "3" ] ; then
+ action="promote"
+ else
+ echo "ERROR: Invalid number of arguments: $#"
+ usage
+ fi ;;
+ i?(nspect))
+ action="inspect"
+ if [ "$num_args" -ne "2" ] ; then
+ echo "ERROR: Invalid number of arguments: $#"
+ usage
+ fi ;;
+ *)
+ echo "ERROR: Invalid option '$1'!"
+ usage ;;
+esac
+shift
+docker login >& /dev/null
+
+# Update local tags
+tags_to_push=""
+for image in "$@" ; do
+ parse_image_name "$image"
+ verify_image_name "$image"
+ get_all_tags_from_dockerhub
+ get_image_id_tags
+ if [ "$action" = "promote" ] ; then
+ if [ -n "$image_name_new" ] ; then
+ promote_new_image
+ else
+ echo "ERROR: No new image specified to promote!"
+ usage
+ fi
+ elif [ "$action" = "revert" ] ; then
+ if [ "$image_version" = "prod" ] ; then
+ revert_prod_image
+ else
+ echo "ERROR: Non-production image '$image' specified!"
+ usage
+ fi
+ else
+ if [ "$image_version" = "prod" ] ; then
+ inspect_images ""
+ else
+ echo "ERROR: Non-production image '$image' specified!"
+ usage
+ fi
+ fi
+done