1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
|
#!/bin/bash
# vi: ts=4 noexpandtab
TMPD=""
VERBOSITY=0
error() { echo "$@" 1>&2; }
errorp() { printf "$@" 1>&2; }
fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
failp() { [ $# -eq 0 ] || errorp "$@"; exit 1; }
debug() {
local level=${1}
shift;
[ "${level}" -gt "${VERBOSITY}" ] && return
echo "$(date): ${@}" 1>&2
}
get_img_entry() {
# get all entries correlating to ami from describe-images output in input
# given valid input, this is the same as 'describe-images ami-id'
local ami="$1"
awk '-F\t' '{
if ($1 == "IMAGE") { if ($2 == ami) cur=1; else cur=0; };
if ( cur == 1 ) print $0 }' "ami=$ami"
}
Usage() {
cat <<EOF
Usage: ${0##*/} [ options ] region image-id
remove 'image-id' from 'region' on ec2.
This includes both unregistering and deleting.
Both operations are permenant, so be careful!
If image-id is kernel or ramdisk, check for other references, failing
if there are any.
-f | --follow if image-id is a machine image, remove
the associated kernel and ramdisk
-n | --dry-run only report what would be done
-s | --skip-referenced if image-id is a kernel or ramdisk and is
referenced by other images, silently continue
-v | --verbose increase verbosity
Example:
${0##*/} us-east-1 ami-abcdef
EOF
}
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; }
cleanup() {
[ -n "${TMPD}" -a -d "${TMPD}" ] && rm -Rf "${TMPD}"
return 0
}
remove_image() {
local short_opts="fhnsv"
local long_opts="cache:,dry-run,follow,following:,help,skip-referenced,unpub-path-fmt:,verbose"
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" || { bad_Usage; return 1; }
local oifs=${IFS};
local region="" id="" img_info="" x=""
local id_info="" manif="" nmanif="" acl="" itype="" kernel="" ramdisk=""
local dry_run=0 follow=0 skip=0 cur="" next="" followed=""
local VERBOSITY=0 # verbosity is local, but read by debug
local unpub_path_fmt="" unpub_path=""
local pt; pt=( ); # remove_image pass through
local rpt; rpt=( ); # remove manifest pass through
while [ $# -ne 0 ]; do
cur=${1}; next=${2};
case "$cur" in
--) shift; break;;
-f|--follow) follow=1; pt[${#pt[@]}]=${cur};;
--following) followed=${next}; shift;;
-h|--help) Usage; return 0;;
-n|--dry-run)
dry_run=1;
pt[${#pt[@]}]=${cur};
rpt[${#rpt[@]}]=${cur};;
-s|--skip-referenced) skip=1; pt[${#pt[@]}]=${cur};;
--unpub-path-fmt)
unpub_path_fmt=${next};
pt[${#pt[@]}]="--unpub-path-fmt=${next}";
shift;;
-v|--verbose)
pt[${#pt[@]}]=${cur};
rpt[${#rpt[@]}]=${cur};
VERBOSITY=$((${VERBOSITY}+1));;
-*) bad_Usage "confused by ${cur}"; return 1;;
esac
shift;
done
region=${1}
id=${2}
[ $# -gt 2 ] && { bad_Usage "to many arguments given"; return 1; }
[ $# -lt 2 ] && { bad_Usage "must provide region and image-id"; return 1; }
if [ -z "${TMPD}" ]; then
TMPD=$(mktemp -d ${TMPDIR:-/tmp}/${0##*/}.XXXXXX) ||
{ error "failed to make temp dir"; return 1; }
trap cleanup EXIT
fi
img_info=${TMPD}/img_info.txt;
xc2 ximages describe-images "--region=$region" --all > "$img_info" ||
fail "failed to describe-images --region=$region --all"
# get manifest, public/private, type, kernel,ramdisk
id_info=$(awk '-F\t' '$2 == id { print $3,$6,$13,$9,$10,$11 }' \
"id=${id}" "OFS=|" "${img_info}" ) &&
{ IFS="|"; set -- ${id_info}; IFS=${oifs}; } ||
error "failed to parse images info"
# ramdisk and kernel can be empty, in which case the 'set based split'
# above will result in 4 items in the list
[ $# -eq 5 -o $# -eq 6 ] ||
{ error "${id} not found in ${region}"; return 1; }
nmanif=${1}; acl=${2}; st_type=${3}; itype=${4}; kernel=${5}; ramdisk=${6};
local snapshots=""
manif=""
if [ "${st_type}" = "ebs" ]; then
local my_snaps="" snap="" others=""
get_img_entry "$id" < "$img_info" > "$TMPD/tmp.out" &&
my_snaps=$(awk '-F\t' '$1 == "BLOCKDEVICEMAPPING" &&
$5 ~ /snap-/ { printf("%s ",$5) }' < "${TMPD}/tmp.out") &&
[ -n "$my_snaps" ] ||
{ error "failed to get snapshot info for ${id}"; return 1; }
# filter out snapshots that are used by other (more than 1) builds
# this occurs when a daily build has been promoted as the promoted
# name re-uses the snapshot
snapshots=""
for snap in $my_snaps; do
others=$(awk '-F\t' '
$1 == "IMAGE" { curami=$2; curname=$3 };
$1 == "BLOCKDEVICEMAPPING" && $5 == snap && curami != ami \
{ printf("%s:%s ",curami,curname); }' \
"snap=$snap" ami="$id" "$img_info") || {
error "failed check for other snaps for $id/$snap";
return 1;
}
if [ -n "$others" ]; then
error "Warn: not deleting $id/$snap. used by others: $others"
else
snapshots="$snapshots $snap"
fi
done
snapshots=${snapshots# }
elif [ "${st_type}" = "instance-store" ]; then
# due to 'ec2-register --name', manifest path isn't in describe-images
# output. need to get it elsewhere
manif=$(xc2 get-manifest-path "${id}" "${region}")
fi
if [ -n "${unpub_path_fmt}" ]; then
unpub_path=${unpub_path_fmt//%r/${region}}
unpub_path=${unpub_path//%i/${id}}
fi
# check if this manifest is registered more than once
local other=""
## TODO: THIS IS BROKEN. To check if manifest path is registered
## more than once, you have to get the manifest path. but
## will not have that, only name
other=$(awk '-F\t' '$3 == manif && $2 != id { print $2 }' \
"manif=${nmanif}" "id=${id}" "${img_info}") ||
{ error "failed to check for multi-registered manifest"; return 1; }
[ -z "${other}" ] ||
{ error "${id}:${nmanif} is multi-registered: ${other}"; return 1; }
# if its a kernel or ramdisk, check public images
if [ "${itype}" = "kernel" -o "${itype}" = "ramdisk" ]; then
local used_by=""
# have to check that the image we find isn't the one followed
local cond='( $10 == id || $11 == id )'
local action='{ printf("\t%s %s\n",$2,$3); }'
[ -z "${followed}" ] || cond="${cond} && \$2 != \"${followed}\""
used_by=$(awk '-F\t' "${cond} ${action}" "id=${id}" "${img_info}" ) ||
{ error "failed to parse image info for ${id}"; return 1; }
if [ -n "${used_by}" ]; then
debugp 2 "%s used by:\n%s\n" "${id}" "${used_by}"
if [ ${skip} -eq 0 ]; then
errorp "%s referenced by public images%s\n" \
"${id}" "${followed:+ [followed from ${followed}]}"
errorp "\t%s\n" " use --skip-referenced to skip"
return 1;
fi
# skip was set, just ignore
debugp 1 "skipping %s %s\n" "${id}" "${nmanif}"
return 0
fi
# if its a machine and follow was given, then follow
elif [ "${itype}" = "machine" ]; then
if [ ${follow} -ne 0 ]; then
for x in "${kernel}" "${ramdisk}"; do
[ -n "${x}" ] || continue;
remove_image "${pt[@]}" --following=${id} "${region}" "${x}" ||
return 1;
done
fi
else
error "bad type: ${itype} for ${id} [${nmanif}]"
return 1
fi
# its safe to delete it now
local out=""
if [ ${dry_run} -eq 0 ]; then
out=$(xc2 ximages deregister --region "${region}" "${id}" ) || {
error "failed to unregister ${id}";
return 1;
}
local snap
for snap in ${snapshots}; do
out=$(xc2 delete-snapshot --region "${region}" "${snap}" ) || {
error "Warn: failed to delete snapshot ${snap}: ${out}"
return 1;
}
done
fi
if [ "${st_type}" = "instance-store" ]; then
remove-manifest --message-label "$id" \
${unpub_path:+--unpub-path=${unpub_path}} \
"${rpt[@]}" "${manif}" ||
{ error "failed to remove manifest ${manif}"; return 1; }
else
local xinfo="$id ${nmanif}${snapshots:+ [${snapshots}]}"
[ $dry_run -eq 0 ] && echo "deleted $xinfo" || error "delete $xinfo"
fi
}
remove_image "$@"
|