Skip to content

Instantly share code, notes, and snippets.

Created February 15, 2017 07:55
Show Gist options
  • Save anonymous/ccd20beca89fb36ecb1aa3fac1d2f9bd to your computer and use it in GitHub Desktop.
Save anonymous/ccd20beca89fb36ecb1aa3fac1d2f9bd to your computer and use it in GitHub Desktop.
#!/bin/bash
set -e
exec > >(tee -a /var/log/eb-cfn-init.log|logger -t [eb-cfn-init] -s 2>/dev/console) 2>&1
PREINIT_CMD='
{
"api_version" : "1.0",
"request_id": "0",
"command_name": "CMD-PreInit"
}
'
SELF_STARTUP_CMD='
{
"api_version" : "1.0",
"request_id": "0",
"command_name": "CMD-SelfStartup"
}
'
function log
{
NANOSEC=`date +%N`
echo -e [`date -u +"%Y-%m-%dT%H:%M:%S"`.${NANOSEC:0:3}Z] "$@"
}
# Emit starting log
log Started EB Bootstrapping Script.
# Update cfn bootstrap to get the latest version of cfn-init etc
#yum update -y system-release
RPMS=$1
TARBALLS=$2
EB_GEMS=$3
SIGNAL_URL=$4
STACK_ID=$5
REGION=$6
GUID=$7
HEALTHD_GROUP_ID=$8
HEALTHD_ENDPOINT=$9
PROXY_SERVER=${10}
HEALTHD_PROXY_LOG_LOCATION=${11}
PARAM_LIST="Received parameters:\n\
RPMS = $RPMS\n\
TARBALLS = $TARBALLS\n\
EB_GEMS = $EB_GEMS\n\
SIGNAL_URL = $SIGNAL_URL\n\
STACK_ID = $STACK_ID\n\
REGION = $REGION\n\
GUID = $GUID\n\
HEALTHD_GROUP_ID = $HEALTHD_GROUP_ID\n
HEALTHD_ENDPOINT = $HEALTHD_ENDPOINT\n
PROXY_SERVER = $PROXY_SERVER\n
HEALTHD_PROXY_LOG_LOCATION = $HEALTHD_PROXY_LOG_LOCATION"
log $PARAM_LIST
# Helper functions
function error_exit
{
cfn-signal 1 "$1"
exit 1
}
function tailog
{
log "Tailing $2"
echo -e "\n******************* $1 taillog *******************"
if [ -f "$2" ]; then
echo -e "$(tail -n 50 $2)"
else
echo -e "***$1 is not available yet.***"
fi
echo -e "******************* End of taillog *******************\n\n"
}
function tail_logs
{
tailog eb-commandprocessor /var/log/eb-commandprocessor.log
tailog eb-activity /var/log/eb-activity.log
tailog eb-tools /var/log/eb-tools.log
tailog eb-version-deployment /var/log/eb-version-deployment.log
tailog cfn-init /var/log/cfn-init.log
tailog cfn-hup /var/log/cfn-hup.log
}
function log_and_exit
{
log $1
# signal success
cfn-signal 0
# Output startup logs into the console.
tail_logs
log Completed EB Bootstrapping Script.
exit 0
}
function is_baked
{
if [[ -f /etc/elasticbeanstalk/baking_manifest/$1 ]]; then
true
else
false
fi
}
function mark_installed
{
mkdir -p /etc/elasticbeanstalk/baking_manifest/
echo `date -u` > /etc/elasticbeanstalk/baking_manifest/$1-manifest
}
SLEEP_TIME=10
SLEEP_TIME_MAX=86400 # One day
function sleep_delay
{
if (( $SLEEP_TIME < $SLEEP_TIME_MAX )); then
log Sleeping $SLEEP_TIME ...
sleep $SLEEP_TIME
SLEEP_TIME=$(($SLEEP_TIME * 2))
else
log Sleeping $SLEEP_TIME_MAX ...
sleep $SLEEP_TIME_MAX
fi
}
function retry_execute
{
log Started executing $@.
SLEEP_TIME=10
while true; do
FN_OUTPUT=""
set +e
"$@"
RESULT=$?
set -e
log Command Returned: "$FN_OUTPUT"
if (( $RESULT != 0 )); then
log "Command return code $RESULT".
tail_logs
sleep_delay
log Retrying...
else
break
fi
done
log Completed executing $1.
}
function install_rpms
{
for RPM_LIB in $@
do
log Installing RPM: $RPM_LIB.
FULL_NAME=${RPM_LIB##*/}
RPM_NAME=${FULL_NAME%.*}
INSTALLED_RPM=$(rpm -q $RPM_NAME)
RESULT=$?
if (( $RESULT == 0 )); then
log $RPM_NAME has already been installed. Skip installing.
else
FN_OUTPUT="$FN_OUTPUT\n\n$(rpm -Uv --nodeps --replacepkgs --oldpackage $RPM_LIB 2>&1)"
RESULT=$?
if [ $RESULT -ne 0 ]; then
return $RESULT
fi
fi
done
}
function install_tarballs
{
for TAR_BALL in $@
do
log Installing tarball: $TAR_BALL.
FULL_NAME=${TAR_BALL##*/}
if is_baked ${FULL_NAME}-manifest; then
log $FULL_NAME has already been installed. Skip installing.
else
FN_OUTPUT="$FN_OUTPUT\n\n$(wget --tries=3 --retry-connrefused -nv -O /tmp/$FULL_NAME $TAR_BALL 2>&1 \
&& tar --no-same-owner --no-same-permissions -C / -xf /tmp/$FULL_NAME 2>&1 1>/dev/null \
&& rm -f /tmp/$FULL_NAME)"
RESULT=$?
if [ $RESULT -ne 0 ]; then
return $RESULT
fi
mark_installed ${FULL_NAME}
fi
done
}
function install_eb_gems
{
mkdir -p /tmp/ebgems
source /opt/elasticbeanstalk/lib/ruby/profile.sh
for GEM in $@
do
log Installing EB Gem: $GEM.
FULL_NAME=${GEM##*/}
GEM_FULL_NAME=${FULL_NAME%.*}
GEM_NAME=${GEM_FULL_NAME%-*}
GEM_VERSION=${GEM_FULL_NAME##*-}
INSTALLED=$(gem query -i -v $GEM_VERSION -i $GEM_NAME)
RESULT=$?
if (( $RESULT == 0)); then
log $GEM_FULL_NAME has already been installed. Skip installing.
else
FN_OUTPUT="$FN_OUTPUT\n\n$(wget --tries=3 --retry-connrefused -nv -O /tmp/ebgems/$FULL_NAME $GEM 2>&1 \
&& gem install -f --no-document /tmp/ebgems/$FULL_NAME 2>&1)"
RESULT=$?
if [ $RESULT -ne 0 ]; then
return $RESULT
fi
fi
done
rm -rf /tmp/ebgems
}
function run_healthd
{
id -u healthd &>/dev/null || useradd -s /sbin/nologin healthd
# Healthd config files
mkdir -p /etc/healthd # root owns
# Healthd logs
mkdir -p /var/log/healthd
chown healthd:healthd /var/log/healthd
# Healthd pid directory
mkdir -p /var/run/healthd
chown healthd:healthd /var/run/healthd
# Healthd base plugin directory
mkdir -p /var/elasticbeanstalk/healthd # root owns
# for reboots
rm -f /etc/healthd/config.yaml
echo "group_id: $HEALTHD_GROUP_ID" >> /etc/healthd/config.yaml
if [ -n "$HEALTHD_ENDPOINT" ]
then
echo "endpoint: $HEALTHD_ENDPOINT" >> /etc/healthd/config.yaml
fi
echo "log_to_file: true" >> /etc/healthd/config.yaml
if [ "$PROXY_SERVER" == "httpd" ]
then
echo "appstat_log_path: /var/log/httpd/healthd/application.log" >> /etc/healthd/config.yaml
echo "appstat_unit: usec" >> /etc/healthd/config.yaml
echo "appstat_timestamp_on: arrival" >> /etc/healthd/config.yaml
elif [ "$PROXY_SERVER" == "nginx" ]
then
echo "appstat_log_path: /var/log/nginx/healthd/application.log" >> /etc/healthd/config.yaml
echo "appstat_unit: sec" >> /etc/healthd/config.yaml
echo "appstat_timestamp_on: completion" >> /etc/healthd/config.yaml
elif [ "$PROXY_SERVER" == "other" ]
then
echo "appstat_log_path: $HEALTHD_PROXY_LOG_LOCATION" >> /etc/healthd/config.yaml
echo "appstat_unit: sec" >> /etc/healthd/config.yaml
echo "appstat_timestamp_on: completion" >> /etc/healthd/config.yaml
fi
cat << EOF1 > /etc/init/healthd.conf
description "Elastic Beanstalk Healthd Upstart Manager"
author "Elastic Beanstalk"
start on runlevel [2345]
stop on runlevel [!2345]
respawn
respawn limit 15 5
script
exec /bin/bash <<"EOF2"
if [ -d /etc/healthd ]
then
source /opt/elasticbeanstalk/lib/ruby/profile.sh
exec su -s /bin/bash -c "healthd" healthd
fi
EOF2
end script
EOF1
# force restart for custom AMIs
initctl restart healthd || initctl start healthd
if ( initctl status healthd | grep stop ); then
initctl start healthd
# wait for healthd to gracefully start first
for i in `seq 1 60`;
do
status_output=`curl -s localhost:22221/status || true`
if [ "$status_output" == '{"status":"ok"}' ]; then
echo $status_output;
break;
fi;
echo $status_output;
echo "Waiting for healthd to start ...";
sleep 0.5;
done
fi
}
function stop_healthd
{
# remove config so it does not restart
rm -rf /etc/healthd
# try to stop if already running
initctl stop healthd || true;
# remove upstart script
rm -rf /etc/init/healthd.conf
}
function cfn_init
{
log Running cfn-init ConfigSet: $1.
if [[ $2 == "first_init" ]]; then
local stackname=$STACK_ID
else
local stackname=$(/opt/elasticbeanstalk/bin/get-config meta -k stackname)
log Using local cached stack name for reboot.
fi
FN_OUTPUT=$(EB_EVENT_FILE=/var/log/eb-startupevents.log EB_SYSTEM_STARTUP=true /opt/aws/bin/cfn-init -s "$stackname" \
-r AWSEBAutoScalingGroup --region "$REGION" --configsets $1 > /var/log/eb-cfn-init-call.log 2>&1 )
}
function cfn-signal
{
local signalurl=$(/opt/elasticbeanstalk/bin/get-config meta -k instance_signal_url)
if [[ -z $signalurl ]]; then
# use default signal url if not specified in metadata
signalurl=$SIGNAL_URL
fi
if [[ -z $2 ]]; then
local reason=""
else
local reason=" -r $2 "
fi
log Sending signal $1 to CFN wait condition $signalurl
/opt/aws/bin/cfn-signal -e $1 $reason "$signalurl" || log 'Wait Condition Signal expired.'
}
function run_eb_command
{
log Running EB Command: $1.
FN_OUTPUT=$(CMD_DATA=$1 /opt/elasticbeanstalk/bin/command-processor -e)
}
function start_cfn_hup
{
log Starting cfn-hup.
FN_OUTPUT=$(start cfn-hup LANG=$LANG)
}
function sync_clock
{
log Synchronizing network time in background.
nohup ntpdate -u 0.amazon.pool.ntp.org 1.amazon.pool.ntp.org 2.amazon.pool.ntp.org 3.amazon.pool.ntp.org &
}
function lock_repo_version
{
if is_baked lock_repo_version_$GUID; then
log yum repo has already been locked to $GUID.
else
log Locking yum repo version to GUID.
mkdir -p /etc/yum/vars
echo $GUID > /etc/yum/vars/guid
chmod 644 /etc/yum/vars/guid
for i in updates preview gpu nosrc hvm graphics; do
local repo_file="/etc/yum.repos.d/amzn-$i.repo"
if [ -f $repo_file ]; then
sed -i -r 's/mirror.list$/mirror.list-$guid/' $repo_file
fi
local template_file="/etc/cloud/templates/amzn-$i.repo.tmpl"
if [ -f $template_file ]; then
sed -i -r 's/mirror.list$/mirror.list-\\$guid/' $template_file
fi
done
yum clean -y all || echo Warning: cannot clean local yum cache. Continue...
mark_installed lock_repo_version_$GUID
log Completed yum repo version locking.
fi
}
function update_yum_packages
{
if is_baked update_yum_packages_$GUID; then
log yum update has already been done.
else
log Updating yum packages.
yum --exclude=aws-cfn-bootstrap update -y || echo Warning: cannot update yum packages. Continue...
mark_installed update_yum_packages_$GUID
log Completed updating yum packages.
fi
}
function create_eb_user_group
{
groupadd -f -r awseb
log Completed creating AWS EB users and groups.
}
#------------- Start of Execution -----------------
sync_clock
# Yum package update
lock_repo_version
update_yum_packages
# create users and groups
create_eb_user_group
## Install Packages ##
retry_execute install_rpms $RPMS
retry_execute install_tarballs $TARBALLS
retry_execute install_eb_gems $EB_GEMS
if [ -n "$HEALTHD_GROUP_ID" ]
then
# enhanced health
log "Starting healthd"
run_healthd
else
# basic health
log "Ensuring healthd is not running"
# could be already running if AMI baked from enhanced health environment
stop_healthd
fi
# branch on if instance already initialized
# set EB_FIRST_RUN only on first run, otherwise empty
if [[ $(/opt/elasticbeanstalk/eb_infra/infra-provision_registrar.rb instance-init check) == "first_init" ]]; then
log First init of instance.
export EB_FIRST_RUN=true
retry_execute cfn_init '_OnInstanceBoot' 'first_init'
/opt/elasticbeanstalk/eb_infra/infra-provision_registrar.rb instance-init mark
else
log Reboot of instance.
retry_execute cfn_init '_OnInstanceReboot'
fi
log Check whether controlled by launch workflow...
export EB_IS_WORKFLOW_RUNNING=false
LAUNCH_S3_URL=$(/opt/elasticbeanstalk/bin/get-config meta -k launchs3url) || log 'Failed to find launch s3 url.'
if [[ "$LAUNCH_S3_URL" ]]; then
if wget -q "$LAUNCH_S3_URL" > /dev/null; then
log Worflow running.
export EB_IS_WORKFLOW_RUNNING=true
fi
fi
if [[ "$EB_IS_WORKFLOW_RUNNING" == "true" ]];
then
log Workflow controlled instance. Running container provisioning...
retry_execute start_cfn_hup
retry_execute run_eb_command "$PREINIT_CMD"
else
log Scaled up instance. Running full self-initiated provisioning.
# Writing startup version to prevent duplicate execution
# retry_execute write_metadata
retry_execute run_eb_command "$PREINIT_CMD"
retry_execute run_eb_command "$SELF_STARTUP_CMD"
retry_execute start_cfn_hup
fi
log_and_exit 'Successfully bootstrapped instance.'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment