@@ -11,20 +11,31 @@ on_error() {
1111 echo " ${BASH_SOURCE[0]} errored with exit code ${exit_code} on line ${error_line} ."
1212
1313 if [[ $exit_code != 0 ]]; then
14- if ! aws autoscaling set-instance-health \
15- --instance-id " $INSTANCE_ID " \
16- --health-status Unhealthy; then
17- echo Failed to set instance health to unhealthy.
14+ # Only try to set instance health if we have an instance ID
15+ # (metadata service must be available for this to work)
16+ if [[ -n " ${INSTANCE_ID:- } " ]]; then
17+ if ! aws autoscaling set-instance-health \
18+ --instance-id " $INSTANCE_ID " \
19+ --health-status Unhealthy; then
20+ echo Failed to set instance health to unhealthy.
21+ fi
22+ else
23+ echo " Skipping instance health check - INSTANCE_ID not available (metadata service may be unavailable)"
1824 fi
1925 fi
2026
21- cfn-signal \
27+ # Attempt to signal CloudFormation failure
28+ # This may fail if metadata service is unavailable, but we try anyway
29+ if ! cfn-signal \
2230 --region " $AWS_REGION " \
2331 --stack " $BUILDKITE_STACK_NAME " \
2432 --reason " Error on line $error_line : $( tail -n 1 /var/log/elastic-stack.log) " \
2533 --resource " AgentAutoScaleGroup" \
26- --exit-code " $exit_code "
34+ --exit-code " $exit_code " ; then
35+ echo " Failed to send cfn-signal (this is expected if metadata service is unavailable)"
36+ fi
2737
38+ # The OnFailure=poweroff.target in cloud-final.service.d will handle instance termination
2839 exit " $exit_code "
2940}
3041
@@ -43,9 +54,76 @@ exec > >(tee -a /var/log/elastic-stack.log | logger -t user-data -s 2>/dev/conso
4354echo " Starting ${BASH_SOURCE[0]} ..."
4455
4556# This needs to happen first so that the error reporting works
46- token=$( curl -X PUT -H " X-aws-ec2-metadata-token-ttl-seconds: 60" --fail --silent --show-error --location http://169.254.169.254/latest/api/token)
47- INSTANCE_ID=$( curl -H " X-aws-ec2-metadata-token: $token " --fail --silent --show-error --location http://169.254.169.254/latest/meta-data/instance-id)
48- echo " Detected INSTANCE_ID=$INSTANCE_ID "
57+ # Fetch metadata with retry logic to handle transient metadata service failures
58+ fetch_metadata_with_retry () {
59+ local max_attempts=5
60+ local timeout=2
61+ local wait_time=1
62+ local max_wait=30
63+ local attempt=1
64+
65+ echo " Fetching EC2 metadata with retry (max attempts: $max_attempts )..."
66+
67+ # Temporarily disable exit on error for retry logic
68+ set +e
69+
70+ while [[ $attempt -le $max_attempts ]]; do
71+ echo " Attempt $attempt of $max_attempts to fetch metadata..."
72+
73+ # Try to get the token
74+ local token
75+ token=$( curl -X PUT -H " X-aws-ec2-metadata-token-ttl-seconds: 60" \
76+ --max-time " $timeout " \
77+ --fail --silent --show-error \
78+ --location http://169.254.169.254/latest/api/token 2>&1 )
79+ local token_result=$?
80+
81+ if [[ $token_result -eq 0 ]]; then
82+ # Try to get the instance ID
83+ INSTANCE_ID=$( curl -H " X-aws-ec2-metadata-token: $token " \
84+ --max-time " $timeout " \
85+ --fail --silent --show-error \
86+ --location http://169.254.169.254/latest/meta-data/instance-id 2>&1 )
87+ local instance_id_result=$?
88+
89+ if [[ $instance_id_result -eq 0 ]]; then
90+ echo " Successfully fetched metadata on attempt $attempt "
91+ echo " Detected INSTANCE_ID=$INSTANCE_ID "
92+ # Re-enable exit on error
93+ set -e
94+ return 0
95+ else
96+ echo " Failed to fetch instance ID: $INSTANCE_ID "
97+ fi
98+ else
99+ echo " Failed to fetch metadata token: $token "
100+ fi
101+
102+ if [[ $attempt -lt $max_attempts ]]; then
103+ echo " Waiting ${wait_time} s before retry..."
104+ sleep " $wait_time "
105+ # Exponential backoff with cap at 30 seconds
106+ wait_time=$(( wait_time * 2 ))
107+ if [[ $wait_time -gt $max_wait ]]; then
108+ wait_time=$max_wait
109+ fi
110+ fi
111+
112+ attempt=$(( attempt + 1 ))
113+ done
114+
115+ # Re-enable exit on error before returning failure
116+ set -e
117+
118+ echo " ERROR: Failed to fetch EC2 metadata after $max_attempts attempts"
119+ echo " This likely indicates the EC2 metadata service is unavailable or this instance has connectivity issues"
120+
121+ # Return failure - this will trigger the error trap and call on_error()
122+ return 1
123+ }
124+
125+ # Fetch metadata or fail (which triggers on_error and poweroff)
126+ fetch_metadata_with_retry
49127
50128# This script is run on every boot so that we can gracefully recover from hard failures (eg. kernel panics) during
51129# any previous attempts. If a previous run is detected as started but not complete then we will fail this run and mark
0 commit comments