summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2020-01-09 19:20:14 +0000
committerMike Crute <mike@crute.us>2020-01-09 19:23:51 +0000
commit4c4e6d896989dee98b7c9672332b8701ebbe41a6 (patch)
tree90af8f46803c29b9485879bcefb50b5618f3c527 /bin
parent1fe8647502560320171b3ff19c34945d23a29c75 (diff)
downloaddotfiles-4c4e6d896989dee98b7c9672332b8701ebbe41a6.tar.bz2
dotfiles-4c4e6d896989dee98b7c9672332b8701ebbe41a6.tar.xz
dotfiles-4c4e6d896989dee98b7c9672332b8701ebbe41a6.zip
Add registry remove script
Diffstat (limited to 'bin')
-rwxr-xr-xbin/remove_image_from_registry.sh315
1 files changed, 315 insertions, 0 deletions
diff --git a/bin/remove_image_from_registry.sh b/bin/remove_image_from_registry.sh
new file mode 100755
index 0000000..d6e9fb1
--- /dev/null
+++ b/bin/remove_image_from_registry.sh
@@ -0,0 +1,315 @@
1#!/bin/bash
2
3IMAGE_ARG=""
4HOST=""
5IMAGE=""
6URI=""
7TAG=""
8USERNAME=""
9CREDENTIALS_STRING=""
10INSECURE=""
11RAW_URL=""
12RAW_HTTP_METHOD=""
13RAW_HTTP_HEADER=""
14TAG_ONLY=false
15
16function printUsage
17{
18 local RESULT=$1
19 if [ "$RESULT" == "" ]; then
20 RESULT=0
21 fi
22 cat << EOF
23
24Usage:
25 $ ./remove_image_from_registry.sh [OPTIONS] [IMAGE]
26
27IMAGE
28 Image name has the format registryhost:port/repository/imagename:version
29 For instance : mydockerregistry:5000/myrepo/zoombie:latest
30 Note that the version tag ("latest" in this example) is mandatory.
31 Please note that this script will delete the image from the repository, not only the tag; if the
32 image you are deleting have multiple tags ( for instance "1.0" and "latest" ), both tags will be
33 removed from the registry.
34 Docker registry does not support deleting only a tag ATM, ref https://github.com/docker/distribution/issues/2317
35 The option "--tag-only" tries to circumvent this by restoring other tags which also disappear from
36 the registry during the delete. However, this is not entirely safe:
37 - Do not delete multiple images concurrently.
38 - Do not run registry garbage collector while deleting images (this you should never do anyway....).
39 - Do not create new local tags for the image during the delete operation.
40
41REQUIREMENTS
42 The registry must run a v2 registry and have token based authentication enabled.
43 Deletion must be enabled on the registry server (REGISTRY_STORAGE_DELETE_ENABLED=true).
44
45NOTE
46 The blobs are actually not deleted from the registry server automatically after running this script.
47 In order to do that you must manually (for the time being) run the registry garbage collector.
48 See https://docs.docker.com/registry/garbage-collection/ for more info about this.
49
50OPTIONS
51 -h, --help
52 Print help
53 --insecure
54 Connect to a registry which has a self-signed SSL certificate
55 -p
56 Prompt for password
57 -u <username>
58 Use the given username when authenticating with the registry
59 --raw <url> <http-method> [http-header]
60 Send custom request to the registry. When using this argument, do not use the [IMAGE] argument too.
61 Example:
62 ./remove_image_from_registry.sh \\
63 -u admin \\
64 --insecure \\
65 --raw \\
66 mydockerregistry:5000/v2/imagename/manifests/latest \\
67 GET \\
68 "Accept: application/vnd.docker.distribution.manifest.v2+json"
69 --tag-only
70 After deleting the image, try to recover all other tags which also pointed to the image
71
72
73Password may also be set using the environment variable REGISTRY_PASSWORD
74 $ export REGISTRY_PASSWORD=sesame
75
76EOF
77 exit $RESULT;
78}
79
80function validateImageName
81{
82 local IMAGE_NAME
83 IMAGE_NAME="$1"
84 if [[ "$IMAGE_NAME" == https://* ]]; then
85 echo "Image name or raw URL should not start with https://"
86 exit 1
87 fi
88 if [[ "$IMAGE_NAME" == http://* ]]; then
89 echo "Image name or raw URL should not start with http://"
90 echo "Anyway, registry must use SSL in order to make token based auth work"
91 exit 1
92 fi
93}
94
95function parseArguments
96{
97 while (( "$#" )); do
98 if [ "$1" = "-u" ]; then
99 shift
100 USERNAME=$1
101 elif [ "$1" = "-p" ]; then
102 echo -n "Password: "
103 read -s REGISTRY_PASSWORD
104 echo
105 elif [ "$1" = "--insecure" ]; then
106 INSECURE=" --insecure"
107 elif [ "$1" = "--help" ]; then
108 printUsage
109 elif [ "$1" = "-h" ]; then
110 printUsage
111 elif [ "$1" = "--raw" ]; then
112 shift
113 RAW_URL="$1"
114 validateImageName "$1"
115 shift
116 RAW_HTTP_METHOD="$1"
117 shift
118 RAW_HTTP_HEADER="$1"
119 elif [ "$1" = "--tag-only" ]; then
120 TAG_ONLY=true
121 else
122 # If first param is a dash, we have an invalid argumwent
123 if [ ${1:0:1} == "-" ]; then
124 echo "Error: Unknown parameter : $1"
125 exit 1
126 fi
127 if [ "$IMAGE_ARG" != "" ]; then
128 echo "Error: You may only provide IMAGE name once"
129 exit 1
130 fi
131 validateImageName "$1"
132 IMAGE_ARG="$1"
133 HOST=`echo $IMAGE_ARG|cut -f 1 -d "/"`
134 IMAGE=`echo $IMAGE_ARG|cut -f 2- -d "/"|cut -f 1 -d ":"`
135 TAG=`echo $IMAGE_ARG|cut -f 2- -d "/"|cut -f 2 -d ":"`
136 fi
137 shift
138 done
139
140 if [ "$IMAGE_ARG" = "" ] && [ "$RAW_URL" = "" ]; then
141 echo "Error: You need to provide image name"
142 printUsage 1
143 fi
144
145 if [ "$USERNAME" != "" ]; then
146 CREDENTIALS_STRING=" --user ${USERNAME}:${REGISTRY_PASSWORD}"
147 fi
148}
149
150# $1 is URL
151# $2 is HTTP METHOD (default GET)
152# $2 is additional header ( optional )
153function sendRegistryRequest
154{
155 local URL
156 local WWW_AUTH_HEADER
157 local TOKEN
158 local TOKEN_RESP
159 local REALM
160 local SERVICE
161 local SCOPE
162 local CUSTOM_HEADER
163 local HTTP_METHOD
164 local CURL_HTTP_METHOD_OPTION
165 local CURL_ARG
166 local RESULT
167
168 URL="$1"
169
170 CURL_HTTP_METHOD_OPTION="-X"
171 if [ "$2" != "" ]; then
172 HTTP_METHOD="$2"
173 else
174 HTTP_METHOD="GET"
175 fi
176
177 # If HTTP_METHOD == "HEAD", we'll need to use -I option instead
178 if [ $HTTP_METHOD = "HEAD" ]; then
179 CURL_HTTP_METHOD_OPTION="-I"
180 HTTP_METHOD=""
181 fi
182
183 if [ "$3" != "" ]; then
184 CUSTOM_HEADER="$3"
185 else
186 CUSTOM_HEADER=""
187 fi
188 WWW_AUTH_HEADER=`curl -sS -i $INSECURE $CURL_HTTP_METHOD_OPTION $HTTP_METHOD -H "Content-Type: application/json" ${URL} |grep Www-Authenticate|sed 's|.*realm="\(.*\)",service="\(.*\)",scope="\(.*\)".*|\1,\2,\3|'`
189
190 REALM=`echo $WWW_AUTH_HEADER|cut -f 1 -d ","`
191 SERVICE=`echo $WWW_AUTH_HEADER|cut -f 2 -d ","`
192 SCOPE=`echo $WWW_AUTH_HEADER|cut -f 3 -d ","`
193
194 TOKEN=`curl -f -sS $INSECURE -G --data-urlencode "service=${SERVICE}" --data-urlencode "scope=${SCOPE}" "${REALM}" -K- <<< $CREDENTIALS_STRING|jq .token|cut -f 2 -d "\""`
195 RESULT=$?
196 if [ $RESULT -ne 0 ] || [ "$TOKEN" == "" ]; then
197 # Run command again (without -f arg) and output message to std err
198 >&2 echo Auth server responded:
199 >&2 curl -sS $INSECURE -G --data-urlencode "service=${SERVICE}" --data-urlencode "scope=${SCOPE}" "${REALM}" -K- <<< $CREDENTIALS_STRING
200 if [ $RESULT -eq 0 ]; then
201 RESULT=42
202 fi
203 exit $RESULT
204 fi
205
206 # We only use -f parameter if we are doing a ordinary delete request
207 # If we are doing raw request, we output both request and response ( including headers )
208 if [ "$RAW_URL" = "" ]; then
209 CURL_ARG="-f "
210 else
211 CURL_ARG="-v "
212 fi
213 if [ "$CUSTOM_HEADER" == "" ]; then
214 curl $CURL_ARG -sS $INSECURE $CURL_HTTP_METHOD_OPTION $HTTP_METHOD -H "Authorization: Bearer $TOKEN" "${URL}"
215 RESULT=$?
216 if [ $RESULT -ne 0 ]; then
217 # Run command again (without -f arg) and output message to std err
218 >&2 curl -sS $INSECURE $CURL_HTTP_METHOD_OPTION $HTTP_METHOD -H "Authorization: Bearer $TOKEN" "${URL}"
219 exit $RESULT
220 fi
221 else
222 curl $CURL_ARG -i -sS $INSECURE $CURL_HTTP_METHOD_OPTION $HTTP_METHOD -H "$CUSTOM_HEADER" -H "Authorization: Bearer $TOKEN" "${URL}"
223 RESULT=$?
224 if [ $RESULT -ne 0 ]; then
225 # Run command again (without -f arg) and output message to std err
226 >&2 curl -i -sS $INSECURE $CURL_HTTP_METHOD_OPTION $HTTP_METHOD -H "$CUSTOM_HEADER" -H "Authorization: Bearer $TOKEN" "${URL}"
227 exit $RESULT
228 fi
229 fi
230}
231
232function getTags
233{
234 TAGS=`sendRegistryRequest https://${HOST}/v2/${IMAGE}/tags/list GET |jq --compact-output .tags`
235 RESULT=$?
236 if [ "$TAGS" == "" ] || [ $RESULT -ne 0 ]; then
237 exit $RESULT
238 fi
239 # imagenames and tags cannot contain special characters or space. So let's just remove that JSON syntax
240 TAGS=${TAGS//\"}
241 TAGS=${TAGS//\[}
242 TAGS=${TAGS//\]}
243 TAGS=${TAGS//,/ }
244}
245
246# $1 is the tag you want to take backup of
247# $2 is tag name used for backup (backup tag)
248function backupLocalImage
249{
250 # If the tag we are going to delete exists locally we need to take a backup of that
251 # then download the image from registry ( remote and local image may not match even though they have the same name )
252 if [ `docker images --format "{{.Repository}}:{{.Tag}}" ${HOST}/${IMAGE}:$1| wc -l` -eq 1 ]; then
253 BACKUP_TAKEN="true"
254 docker tag ${HOST}/${IMAGE}:$1 ${HOST}/${IMAGE}:$2
255 docker rmi ${HOST}/${IMAGE}:$1
256 fi
257}
258
259# $1 is the tag you want to restore to
260# $2 is the tag name used when taking the backup (backup tag)
261function restoreBackup
262{
263 if [ `docker images --format "{{.Repository}}:{{.Tag}}" ${HOST}/${IMAGE}:$2| wc -l` -eq 1 ]; then
264# docker rmi ${HOST}/${IMAGE}:$1
265 docker tag ${HOST}/${IMAGE}:$2 ${HOST}/${IMAGE}:$1
266 docker rmi ${HOST}/${IMAGE}:$2
267 fi
268}
269
270parseArguments "$@"
271
272if [ "$RAW_URL" = "" ]; then
273 if [ "$TAG_ONLY" = "true" ]; then
274 getTags
275 backupLocalImage $TAG remove_image_from_registry1
276 docker pull ${HOST}/${IMAGE}:${TAG}
277 fi
278 SHA_REQ=`sendRegistryRequest https://${HOST}/v2/${IMAGE}/manifests/${TAG} GET "Accept: application/vnd.docker.distribution.manifest.v2+json"`
279 RESULT=$?
280 if [ "$SHA_REQ" == "" ] || [ $RESULT -ne 0 ]; then
281 docker rmi ${HOST}/${IMAGE}:${TAG}
282 restoreBackup $TAG remove_image_from_registry1
283 exit $RESULT
284 fi
285
286 SHA=$(echo "$SHA_REQ"|grep "Docker-Content-Digest:"|cut -f 2- -d ":"|tr -d '[:space:]')
287 sendRegistryRequest https://${HOST}/v2/${IMAGE}/manifests/${SHA} DELETE
288 if [ "$TAG_ONLY" = "true" ]; then
289 OLDTAGS="$TAGS"
290 getTags
291 TAGSPIPE="|${TAGS// /|}|"
292
293 for i in $OLDTAGS; do
294 # Clearly, we expect TAG to be gone
295 # We don't want to restore that one
296 if [ "$i" = "$TAG" ]; then
297 continue
298 fi
299
300 if [[ $TAGSPIPE != *"|$i|"* ]]; then
301 echo This tag needs to be restored : ${HOST}/${IMAGE}:${i}
302 backupLocalImage $i remove_image_from_registry2
303 docker tag ${HOST}/${IMAGE}:${TAG} ${HOST}/${IMAGE}:${i}
304 docker push ${HOST}/${IMAGE}:${i}
305 docker rmi ${HOST}/${IMAGE}:${i}
306 restoreBackup $i remove_image_from_registry2
307 fi
308 done
309 docker rmi ${HOST}/${IMAGE}:${TAG}
310 restoreBackup $TAG remove_image_from_registry1
311 fi
312else
313 sendRegistryRequest "https://${RAW_URL}" "${RAW_HTTP_METHOD}" "${RAW_HTTP_HEADER}"
314fi
315