Skip to content

Instantly share code, notes, and snippets.

@seanslma
Created May 22, 2024 03:08
Show Gist options
  • Save seanslma/05c2176e18b8f33b34a1af8f059b4562 to your computer and use it in GitHub Desktop.
Save seanslma/05c2176e18b8f33b34a1af8f059b4562 to your computer and use it in GitHub Desktop.
#!/bin/bash
# This script remove tags from local registry, keeping just the n most recents tags
#
# based on: https://gist.github.com/jaytaylor/86d5efaddda926a25fa68c263830dac1?permalink_comment_id=4732942#gistcomment-4732942
# and on: https://stackoverflow.com/questions/31251356/how-to-get-a-list-of-images-on-docker-registry-v2
# Abels changes:
# - Not using jq utility, just curl and native linux binaries
# - Check based tag count, removing last n versions
# Sean's changes:
# - Add timestamp option - only delete tags earlier than the timestamp
# - Add delete option - default to dryrun
# - Add help option
# Function to check if has more than n tags and remove
check_and_delete_registry_digests() {
# Get tag list, not sorted
local tagurl="https://${registry}/v2/${image}/tags/list"
echo "Registry : ${registry}"
echo "Image name: ${image}"
echo
local tags=$(curl -k -s -u $user:$password "$tagurl" | awk -F: '{print $3}' | sed -e 's/[][]//g' -e 's/\"//g' -e 's/ //g' -e 's/,/\n/g' | tr '}' '\n' | tr '{' '\n')
# Filter tags that has this string (optional)
if [ -n "$only" ]; then
tags=$(echo "${tags}" | grep "$only")
fi
# Exclude tags that has this string (optional)
if [ -n "$exclude" ]; then
tags=$(echo "${tags}" | grep -v "$exclude")
fi
local tagsWithTS=""
local keepTS=0
if [ -n "$tags" ]; then
# timestamp
if [ -n "$timestampMin" ]; then
local timestamp_min=$(date -d "$timestampMin" +%s)
fi
# Get created timestamp for each tag
for tag in $tags; do
local created_time=$(curl -k -s -u $user:$password -H 'Accept: application/vnd.docker.distribution.manifest.v1+json' -X GET https://$registry/v2/$image/manifests/$tag | grep -i v1Compatibility | head -n1 | awk -F'created' '{print $2}' | awk -F'"' '{print $3}' | sed -e 's/\\//g')
created_time=$(echo $created_time | sed -e 's/T/ /g' | awk -F'.' '{print $1}')
local created_timestamp=$(date -d "$created_time" +%s)
if [ -n "$timestampMin" ]; then
if [ "$created_timestamp" -lt "$timestamp_min" ]; then
tagsWithTS="${tagsWithTS}${created_timestamp}|${tag} "
else
((keepTS++))
fi
else
tagsWithTS="${tagsWithTS}${created_timestamp}|${tag} "
fi
done
#Sort tags by time stamp and remove from list keeplast values, keeping tags to remove
if [ "$keepTS" -gt "$keeplast" ]; then
keeplast=0
else
keeplast=$((keeplast - keepTS))
fi
tagsWithTS=$(echo $tagsWithTS | xargs -n1 | sort -r -n -t'|' -k1)
tagsWithTS=$(echo $tagsWithTS | xargs -n1 | sed -n $((keeplast + 1)),1000p)
# Tags to remove
local lasttagdigest=""
if [ -n "$tagsWithTS" ]; then
echo "The following image digests will be deleted:"
for tagWithTS in $tagsWithTS; do
timestamp=$(echo "$tagWithTS" | awk -F'|' '{print $1}')
formatted_timestamp=$(date -d "@$timestamp" +%Y-%m-%d\ %H:%M:%S)
tag=$(echo "$tagWithTS" | awk -F'|' '{print $2}')
local tagdigest=$(curl -s -k -I -u "$user:$password" -H "Accept: application/vnd.docker.distribution.manifest.v2+json" "https://${registry}/v2/${image}/manifests/${tag}" | grep -i 'docker-content-digest: ' | awk -F': ' '{print $2}' | sed 's/[\r\n]//g')
local url="https://${registry}/v2/${image}/manifests/${tagdigest}"
if [ "$delete" = true ]; then
if [ "$tagdigest" != "$lasttagdigest" ]; then
curl -s -k -u "$user:$password" -X DELETE $url
fi
echo " Deleted tag: ${formatted_timestamp} - ${image}:${tag}"
else
echo " ${formatted_timestamp} - ${image}:${tag}"
fi
done
else
echo "No image digests to be deleted"
fi
else
echo "No tags found for image: ${image}"
fi
}
HELPMSG="
Basic usage
$0 -r {registry_url} -i {image-name}
Options avaliable
-h help on how to use the command
-r (required) registry url
-i (required) image name
-k keep last n (default 10)
-t keep tags with timestamp >= t
-o keep only tags that contains this string
-e exclude tags if name contains this string
-u user
-p password
-d delete the manifests, otherwise dryrun to only show info
Examples
$0 -r registry.local:5000 -i project1 -d
$0 -r registry.local:5000 -i project1 -e latest -d
"
# Initialize variables
registry=""
image=""
keeplast=""
timestampMin=""
only=""
exclude=""
user=""
password=""
delete=false
while getopts "hr:i:k:t:o:e:u:p:d" opt; do
case "$opt" in
h) echo "$HELPMSG"; exit 0 ;;
r) registry="$OPTARG" ;;
i) image="$OPTARG" ;;
k) keeplast="$OPTARG" ;;
t) timestampMin="$OPTARG" ;;
o) only="$OPTARG" ;;
e) exclude="$OPTARG" ;;
u) user="$OPTARG" ;;
p) password="$OPTARG" ;;
d) delete=true ;;
\?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
esac
done
# Set keeplast default value to 10
if [ -z "$keeplast" ]; then keeplast=5; fi
# Required params
if [ -z "$registry" ]; then echo "Error: Registry (-r) required"; echo "$HELPMSG"; exit 1; fi
if [ -z "$image" ]; then echo "Error: Image (-i) required"; echo "$HELPMSG"; exit 1; fi
check_and_delete_registry_digests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment