~philroche/ubuntu-on-ec2/ec2-publishing-scripts-kernel-panic-revert

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 "$@"