Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 26 additions & 16 deletions ci/tasks/light-aws/cleanup-ami.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,28 @@ if [ -n "${ami_role_arn:-}" ]; then
export AWS_PROFILE=resource_account
fi

# Every stemcell pipeline runs the published sweep against the same account, so two
# pipelines can select the same AMI before either has deleted it. This handles
# expected potential errors that could occur on a collision.
delete_tolerating_missing() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should this be named something like: run_tolerate_missing_errors or something that doesn't specifically mention delete since its also used for deregister.

local output
if output="$("$@" 2>&1)"; then
[ -n "${output}" ] && echo "${output}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: echo should probably be printf

return 0
fi

case "${output}" in
*InvalidAMIID.Unavailable*|*InvalidAMIID.NotFound*|*InvalidSnapshot.NotFound*)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gemini seems to think there is 1 more condition which may be worth a looksie at:

InvalidSnapshot.InUse Race Condition
When multiple pipelines are sweeping AMIs, they usually follow two steps:

  1. Deregister the AMI.
  2. Delete the underlying EBS Snapshots.

Because AWS is eventually consistent, if Pipeline A deregisters the AMI, AWS takes a few moments to "unlock" the underlying snapshot. If Pipeline B hits the snapshot deletion step during this window, AWS will throw InvalidSnapshot.InUse (not NotFound). If this function doesn't tolerate InUse in a concurrent environment, Pipeline B will fail completely.

echo " already deleted by another run, skipping"
return 0
;;
*)
echo "${output}" >&2
return 1
;;
esac
}

past_due=$(date --date="${ami_older_than_days} days ago" +"%Y-%m-%d")
# shellcheck disable=SC2016
past_due_query='sort_by(Images,&CreationDate)[?CreationDate<`'"${past_due}"'`].{ImageId: ImageId, date:CreationDate, SnapshotId: BlockDeviceMappings[0].Ebs.SnapshotId,Version: Tags[?Key==`name`]|[0].Value}'
Expand Down Expand Up @@ -59,16 +81,6 @@ for region in ${ami_destinations}; do
ami_list=$(jq -s '.[0] + .[1]' <(echo "${ami_list}") <(echo "${results}"))
fi

if [ -n "${snapshot_id:-}" ]; then
Comment thread
ystros marked this conversation as resolved.
results=$(aws ec2 describe-images \
--owners self \
--output json \
--region "${region}" \
--filters "Name=block-device-mapping.snapshot-id,Values=${snapshot_id}" \
--query "${past_due_query}" | jq 'reverse | del(.[range(env.ami_keep_latest|tonumber)])')
ami_list=$(jq -s '.[0] + .[1]' <(echo "${ami_list}") <(echo "${results}"))
fi

# 'ami_list' is a json array of objects, each object is an ami and its snapshot
for row in $(echo "${ami_list}" | jq -r '.[] | @base64'); do
_jq() {
Expand All @@ -83,14 +95,12 @@ for region in ${ami_destinations}; do
Snapshot id: $(_jq '.SnapshotId')
"

aws ec2 deregister-image \
delete_tolerating_missing aws ec2 deregister-image \
--image-id "$(_jq '.ImageId')" \
--region "${region}"

if [ "${snapshot_id:-}" != "$(_jq '.SnapshotId')" ]; then
aws ec2 delete-snapshot \
--snapshot-id "$(_jq '.SnapshotId')" \
--region "${region}"
fi
delete_tolerating_missing aws ec2 delete-snapshot \
--snapshot-id "$(_jq '.SnapshotId')" \
--region "${region}"
done
done
1 change: 0 additions & 1 deletion ci/tasks/light-aws/cleanup-ami.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ params:
ami_older_than_days: "60" # Number of days AMI to keep excluding those currently being running
ami_keep_latest: "5" # Number of previous AMI to keep excluding those currently being running
os_name: "" # e.g ubuntu-jammy
snapshot_id: "" # Snapshot id to delete
remove_public_images: "false"
Loading