From c7887ce80ce62512904fc4cc0a1f44492c9ff432 Mon Sep 17 00:00:00 2001 From: Michael Prokop Date: Fri, 20 Dec 2019 17:52:43 +0100 Subject: [PATCH] TT#71608 Port from ec2-api-tools to awscli ec2-api-tools are deprecated since quite some time and they don't support Java v11 (as present on Debian/buster). So let's (finally!) port our code from ec2-api-tools to awscli. awscli gives us the option to use json output. This makes it possible to parse it properly, instead of relying on the position of some key within some section. While at it update the defaults for BASE_AMI and INSTANCE_TYPE to current sane settings and update the coding style to match current best practices (like "${FOOBAR}" instead of "$FOOBAR") and fix shellcheck issues. Nowadays there are also several new AWS regions (namely ca-central-1, ap-east-1, ap-northeast-2, ap-south-1, eu-north-1, eu-west-2, eu-west-3, me-south-1 + us-east-2) which we could enable but don't *yet*. Change-Id: I147c8a6c2ae1fca4e680df8d2fde170b2f33f856 --- ec2-create-ce | 412 +++++++++++++++++++++++++--------------------- ec2-publish-ami | 46 ++---- ec2-stop-instance | 53 ++---- 3 files changed, 251 insertions(+), 260 deletions(-) diff --git a/ec2-create-ce b/ec2-create-ce index 6f47497..aa1616f 100755 --- a/ec2-create-ce +++ b/ec2-create-ce @@ -1,22 +1,14 @@ #!/bin/bash -export EC2_HOME="/usr/share/ec2/ec2-api-tools" -export JAVA_HOME="$(dirname "$(dirname "$(readlink /etc/alternatives/java)")")" -export PATH="$PATH:${EC2_HOME}/bin" export TZ="UTC" # defaults -[ -n "$KEY_NAME" ] || KEY_NAME="jenkins-ngcp-create-ami" -[ -n "$KEY_FILE" ] || KEY_FILE="${HOME}/.ssh/${KEY_NAME}.pem" -[ -n "$SECURITY_GROUP" ] || SECURITY_GROUP="sipwise-only" -[ -n "$TAG_PURPOSE" ] || TAG_PURPOSE="ngcp-create-ce-ami" +[ -n "${KEY_NAME}" ] || KEY_NAME="jenkins-ngcp-create-ami" +[ -n "${KEY_FILE}" ] || KEY_FILE="${HOME}/.ssh/${KEY_NAME}.pem" +[ -n "${SECURITY_GROUP}" ] || SECURITY_GROUP="sipwise-only" +[ -n "${TAG_PURPOSE}" ] || TAG_PURPOSE="ngcp-create-ce-ami" DATE_STRING=$(date +%Y-%m-%d_%H:%M) -# retrieve AWS_* environment variables -if [ -r "$HOME/.ec2-create-ngcp" ] ; then - source "$HOME/.ec2-create-ngcp" -fi - rm -f ec2_report.txt usage() { @@ -67,19 +59,19 @@ Mandatory options for VPC usage: # generate report for usage in other scripts/jenkins jobs generate_ec2_report() { cat > ec2_report.txt << EOF -instance_hostname=$HOSTNAME -instance_id=$INSTANCE_ID -aws_region=$AWS_REGION -ami_id=$AMI_ID -ami_name=$AMI_NAME -ami_description=$AMI_DESCRIPTION -ami_public=$_opt_public -ami_instance_type=$INSTANCE_TYPE -ngcp_version=$NGCP_VERSION +instance_hostname=${HOSTNAME} +instance_id=${INSTANCE_ID} +aws_region=${AWS_REGION} +ami_id=${AMI_ID} +ami_name=${AMI_NAME} +ami_description=${AMI_DESCRIPTION} +ami_public=${_opt_public} +ami_instance_type=${INSTANCE_TYPE} +ngcp_version=${NGCP_VERSION} # for usage in Jenkins -testhost=$TESTHOST -vmversion=$VMVERSION +testhost=${TESTHOST} +vmversion=${VMVERSION} EOF } @@ -91,13 +83,14 @@ bailout() { # to Jenkins downstream job ec2-ami-stop generate_ec2_report - exit "$EXIT" + exit "${EXIT}" } -trap bailout 1 2 3 3 6 9 14 15 +trap bailout 1 2 3 3 6 14 15 # NOTE: this is non-blocking currently, so we're not waiting until everything is finished here copy_ami() { - local logfile=$(mktemp) + local logfile + logfile=$(mktemp) local source_region="$1" local ami_id="$2" @@ -105,33 +98,55 @@ copy_ami() { local ami_description="$4" local target_region - for target_region in us-east-1 us-west-2 us-west-1 eu-central-1 eu-west-1 ap-southeast-1 ap-southeast-2 ap-northeast-1 sa-east-1 ; do - case "$target_region" in - "$source_region") - echo "*** Skipping region $target_region as source and destination region are the same ones. ***" - echo "AMI ID for region ${target_region}: ${ami_id}" >> ec2_report_amis.txt - ;; + for target_region in \ + ap-northeast-1 \ + ap-southeast-1 \ + ap-southeast-2 \ + eu-central-1 \ + eu-west-1 \ + sa-east-1 \ + us-east-1 \ + us-west-1 \ + us-west-2 + ## currently unused ones: + # ap-east-1 \ + # ap-northeast-2 \ + # ap-south-1 \ + # ca-central-1 \ + # eu-north-1 \ + # eu-west-2 \ + # eu-west-3 \ + # me-south-1 \ + # us-east-2 \ + do + case "${target_region}" in + "${source_region}") + echo "*** Skipping region ${target_region} as source and destination region are the same ones. ***" + echo "AMI ID for region ${target_region}: ${ami_id}" >> ec2_report_amis.txt + ;; *) - remove_ami "$target_region" "$ami_name" - - echo "*** Copying AMI ID $ami_id from $source_region to $target_region (note: we are NOT blocking/waiting until this is fully finished!) ***" - ec2-copy-image --source-region "${source_region}" --region "${target_region}" --source-ami-id "$ami_id" -n "$ami_name" -d "$ami_description" > "$logfile" - if [ $? -ne 0 ]; then - echo "Error copying AMI ID $ami_id from $source_region to $target_region" >&2 - return 1 - fi + remove_ami "${target_region}" "${ami_name}" - local ami_id_copy="$(awk '/^IMAGE/ {print $2}' "$logfile")" + echo "*** Copying AMI ID ${ami_id} from ${source_region} to ${target_region} (note: we are NOT blocking/waiting until this is fully finished!) ***" + aws ec2 copy-image --source-image-id "${ami_id}" --source-region "${source_region}" --region "${target_region}" --name "${ami_name}" --description "${ami_description}" > "${logfile}" - if [ -z "$ami_id_copy" ] ; then - echo "Error retrieving AMI ID $ami_id for region ${target_region}:" - cat "$logfile" >&2 - return 1 - fi - - # for usage as global report - echo "AMI ID for region ${target_region}: $ami_id_copy" >> ec2_report_amis.txt - ;; + if [ $? -ne 0 ]; then + echo "Error copying AMI ID ${ami_id} from ${source_region} to ${target_region}" >&2 + return 1 + fi + + local ami_id_copy + ami_id_copy="$(jq -r '.ImageId' "${logfile}")" + + if [ -z "${ami_id_copy}" ] ; then + echo "Error retrieving AMI ID ${ami_id} for region ${target_region}:" + cat "${logfile}" >&2 + return 1 + fi + + # for usage as global report + echo "AMI ID for region ${target_region}: ${ami_id_copy}" >> ec2_report_amis.txt + ;; esac done } @@ -140,40 +155,43 @@ remove_ami() { local aws_region="$1" local ami_name="$2" - if ! ec2-describe-images --region "$aws_region" --filter "name=${ami_name}" | grep -q . ; then + if ! aws ec2 describe-images --region "${aws_region}" --filter "Name=name,Values=${ami_name}" | jq -r '.Images[].ImageId' | grep -q . ; then return 0 # no AMIs with given name present fi echo "*** Warning, AMI with name ${ami_name} exists already." - if ! $_opt_remove_existing_ami ; then + if ! ${_opt_remove_existing_ami} ; then return 0 fi - local existing_ami_id=$(ec2-describe-images --region "$aws_region" --filter "name=${ami_name}" | awk '/IMAGE/ {print $2}') + local existing_ami_id + existing_ami_id=$(aws ec2 describe-images --region "${aws_region}" --filter "Name=name,Values=${ami_name}" | jq -r '.Images[].ImageId') - if [ -z "$existing_ami_id" ] ; then + if [ -z "${existing_ami_id}" ] ; then echo "Problem retrieving AMI ID for AMI with name ${ami_name}." >&2 return 1 fi # we need to identify which snapshot is used by the AMI so we can remove it after "deregister"-ing the AMI - local ami_snapshot=$(ec2-describe-images --region "$aws_region" --filter "name=${ami_name}" | awk '/^BLOCKDEVICEMAPPING.*EBS/ {print $4}') - if [ -z "$ami_snapshot" ] ; then + local ami_snapshot + ami_snapshot=$(aws ec2 describe-images --region "${aws_region}" --filter "Name=name,Values=${ami_name}" | jq -r '.Images[].BlockDeviceMappings[].Ebs.SnapshotId') + + if [ -z "${ami_snapshot}" ] ; then echo "Problem retrieving snapshot ID for AMI with name ${ami_name}." >&2 return 1 fi echo "*** Option --remove-existing-ami is set, removing existing AMI with ID ${existing_ami_id} ***" - ec2-deregister --region "$aws_region" "${existing_ami_id}" + aws ec2 deregister-image --region "${aws_region}" --image-id "${existing_ami_id}" if [ $? -ne 0 ] ; then echo "Noticed problem when trying to delete AMI with name ${ami_name}." >&2 return 1 fi - if $_opt_keep_ami_snapshot ; then + if ${_opt_keep_ami_snapshot} ; then echo "*** Option --keep-ami-snapshot is set, not removing AMI snapshot ${ami_snapshot}. ***" else - ec2-delete-snapshot --region "$aws_region" "${ami_snapshot}" + aws ec2 delete-snapshot --region "${aws_region}" --snapshot-id "${ami_snapshot}" if [ $? -ne 0 ] ; then echo "Noticed problem when trying to delete snapshot ${ami_snapshot} for 'deregister'-ed AMI with name ${ami_name}." >&2 return 1 @@ -183,12 +201,12 @@ remove_ami() { CMDLINE_OPTS=allocation-id:,ami-name:,base-ami:,copy-to-all-regions,elastic-ip:,help,installer-url:,instance-type:,keep-ami-snapshot,ngcp-release:,public,region:,remove-existing-ami,subnet:,skip-reboot -_opt_temp=$(getopt --name "$0" -o +bch --long $CMDLINE_OPTS -- "$@") +_opt_temp=$(getopt --name "$0" -o +bch --long ${CMDLINE_OPTS} -- "$@") if [ $? -ne 0 ]; then echo "Try '$0 --help' for more information." >& 2 exit 1 fi -eval set -- "$_opt_temp" +eval set -- "${_opt_temp}" _opt_keep_ami_snapshot=false _opt_public=false @@ -199,7 +217,7 @@ _opt_copy_to_all_regions=false while :; do case "$1" in --allocation-id) - shift; ALLOCATION_ID="$1" # TODO eipalloc-0a96e07d4355e12fb + shift; ALLOCATION_ID="$1" ;; --ami-name) shift; AMI_NAME="$1" @@ -255,21 +273,16 @@ while :; do done # if unset set sane defaults -[ -n "$AWS_REGION" ] || AWS_REGION="eu-west-1" -[ -n "$BASE_AMI" ] || BASE_AMI="ami-8a745cf3" -[ -n "$INSTANCE_TYPE" ] || INSTANCE_TYPE="t2.medium" - -if [ -z "$AWS_ACCESS_KEY" ] ; then - echo "AWS_ACCESS_KEY is unset, can not continue." >&2 - exit 1 -fi +[ -n "${AWS_REGION}" ] || AWS_REGION="eu-west-1" +[ -n "${BASE_AMI}" ] || BASE_AMI="ami-0e9cc061cd3259f22" +[ -n "${INSTANCE_TYPE}" ] || INSTANCE_TYPE="m5.large" -if [ -z "$AWS_SECRET_KEY" ] ; then - echo "AWS_SECRET_KEY is unset, can not continue." >&2 +if ! [ -r "${HOME}"/.aws/config ] ; then + echo "awscli seems to be unconfigured, ensure ${HOME}/.aws/config exists." >&2 exit 1 fi -if ! [ -r "$KEY_FILE" ] ; then +if ! [ -r "${KEY_FILE}" ] ; then echo "Could not read key file ${KEY_FILE}, can not continue." >&2 exit 1 fi @@ -277,20 +290,25 @@ fi check4progs(){ local RC='' for arg in "$@" ; do - which "$arg" >/dev/null 2>&1 || RC="$arg" + which "${arg}" >/dev/null 2>&1 || RC="${arg}" done - if [ -n "$RC" ] ; then - echo "$RC not found/executable" >&2 + if [ -n "${RC}" ] ; then + echo "${RC} not found/executable" >&2 return 1 fi } -if ! check4progs ec2-run-instances || ! check4progs ec2-create-image || ! check4progs ec2-describe-instances ; then - echo "Required tools not found, forgot to install ec2-api-tools?" >&2 +if ! check4progs jq ; then + echo "Required tool jq not found, forgot to install jq?" >&2 + exit 1 +fi + +if ! check4progs aws ; then + echo "Required tool aws not found, forgot to install awscli?" >&2 exit 1 fi -if [ -z "$NGCP_RELEASE" ] ; then +if [ -z "${NGCP_RELEASE}" ] ; then NGCP_RELEASE="latest" echo "No ngcp release set, defaulting to ${NGCP_RELEASE}" fi @@ -307,23 +325,24 @@ fi if dpkg --info /tmp/ngcp-installer.deb | grep -q 'mr' ; then NGCP_VERSION=$(dpkg --info /tmp/ngcp-installer.deb | awk '/^ Version: / {print $2}' | sed 's/.*mr/mr/') else # older versions don't have the mr string in the version information, so let's reuse provided ngcp release - NGCP_VERSION="$NGCP_RELEASE" + NGCP_VERSION="${NGCP_RELEASE}" echo "*** No 'mr' string inside version information of installer found... ***" echo "*** ... falling back to ngcp release version information [${NGCP_VERSION}] ***" fi -if [ -z "$NGCP_VERSION" ] ; then +if [ -z "${NGCP_VERSION}" ] ; then echo "Couldn't identify ngcp version, exiting." >&2 bailout 1 fi # ensure we don't have any builds running already, see TT#30565 -NUM_RUNNING_INSTANCES=$(ec2-describe-instances --region "${AWS_REGION}" \ - --filter "instance-state-name=running" \ - --filter "tag:Purpose=${TAG_PURPOSE}" 2>/dev/null | \ - grep -c '^INSTANCE') -if [ "$NUM_RUNNING_INSTANCES" -gt 0 ] ; then - echo "Error: there are running instances with tag $TAG_PURPOSE in region $AWS_REGION" >&2 +NUM_RUNNING_INSTANCES=$(aws ec2 describe-instances --region "${AWS_REGION}" \ + --filter "Name=instance-state-name,Values=running" \ + "Name=tag:Purpose,Values=${TAG_PURPOSE}" | \ + jq '.Reservations[].Instances[].InstanceId' | \ + wc -l) +if [ "${NUM_RUNNING_INSTANCES}" -gt 0 ] ; then + echo "Error: there are running instances with tag ${TAG_PURPOSE} in region ${AWS_REGION}" >&2 echo "Exiting to ensure we don't have instances running due to Jenkins job failures." >&2 echo "Please ensure that those instances are stopped before trying to build new ones." >&2 echo "In case of questions contact someone with according AWS permissions." >&2 @@ -332,16 +351,16 @@ fi # if AMI_NAME is set to "none" then we get defaults from Jenkins, if # so then use the ngcp-ce-... naming schema -if [ -n "$AMI_NAME" ] && [ "$AMI_NAME" != "none" ] ; then - echo "*** AMI_NAME is set to $AMI_NAME ***" +if [ -n "${AMI_NAME}" ] && [ "${AMI_NAME}" != "none" ] ; then + echo "*** AMI_NAME is set to ${AMI_NAME} ***" else # use just "ngcp-ce-mr3.3.1.0" if we get something like # "ngcp-ce-mr3.3.1.0+0~20140528091259.443+wheezy~1.gbpf74599" # as NGCP_VERSION AMI_NAME="ngcp-ce-${NGCP_VERSION%%\+*}" - echo "*** AMI_NAME is unset yet, defaulting to $AMI_NAME" + echo "*** AMI_NAME is unset yet, defaulting to ${AMI_NAME}" - case "$AMI_NAME" in + case "${AMI_NAME}" in ngcp-ce-mr*.*.*.*) echo "*** Identified hotfix version string in AMI name [${AMI_NAME}]... ***" AMI_NAME="${AMI_NAME%.*}" @@ -353,19 +372,19 @@ fi AMI_DESCRIPTION="Official sip:provider CE AMI for release ${NGCP_VERSION} [${DATE_STRING}]" -case "$NGCP_RELEASE" in +case "${NGCP_RELEASE}" in 2.*|3.*|mr3.*|mr4.*|mr5.*|mr6.0*|mr6.1*) - echo "*** ngcp release $NGCP_RELEASE doesn't support systemd" + echo "*** ngcp release ${NGCP_RELEASE} doesn't support systemd" SYSTEMD_SUPPORT=false ;; *) - echo "*** ngcp release $NGCP_RELEASE supports systemd" + echo "*** ngcp release ${NGCP_RELEASE} supports systemd" SYSTEMD_SUPPORT=true ;; esac USER_DATA_FILE="$(mktemp)" -cat > "$USER_DATA_FILE" << EOF +cat > "${USER_DATA_FILE}" << EOF #!/bin/bash wget -O /tmp/ngcp-installer.deb ${INSTALLER_URL} dpkg -i /tmp/ngcp-installer.deb @@ -376,7 +395,7 @@ if [ -f /etc/apt/sources.list.d/backports.list ] && grep -q 'jessie-backports' / rm -f /etc/apt/sources.list.d/backports.list fi -if [ "$SYSTEMD_SUPPORT" = "false" ] && [ -d /run/systemd/system ] ; then +if [ "${SYSTEMD_SUPPORT}" = "false" ] && [ -d /run/systemd/system ] ; then echo "Switching from systemd to sysvinit (pre-reboot)" apt update apt install --yes sysvinit-core @@ -431,66 +450,73 @@ FORCE=yes ngcp-installer 2>&1 | tee -a /var/log/ngcp-installer-debug.log EOF fi +echo "*** Generated the following user-data file:" +cat "${USER_DATA_FILE}" +echo "*** end of user-data-file ***" + LOGFILE_RUN="$(mktemp)" -if [ -n "$SUBNET" ] ; then - echo "Starting EC2 instance in region $AWS_REGION, using VPC with subnet $SUBNET, instance-type $INSTANCE_TYPE + AMI $BASE_AMI" - ec2-run-instances --region "$AWS_REGION" \ - --user-data-file "$USER_DATA_FILE" \ - --subnet "$SUBNET" \ - --key "$KEY_NAME" \ - --instance-type "$INSTANCE_TYPE" \ - "$BASE_AMI" >"$LOGFILE_RUN" +if [ -n "${SUBNET}" ] ; then + echo "Starting EC2 instance in region ${AWS_REGION}, using VPC with subnet ${SUBNET}, instance-type ${INSTANCE_TYPE} + AMI ${BASE_AMI}" + aws ec2 run-instances --region "${AWS_REGION}" \ + --user-data "file://${USER_DATA_FILE}" \ + --subnet "${SUBNET}" \ + --key "${KEY_NAME}" \ + --instance-type "${INSTANCE_TYPE}" \ + --image-id "${BASE_AMI}" >"${LOGFILE_RUN}" else - echo "Starting EC2 instance in region $AWS_REGION, using EC2-Classic with security-group $SECURITY_GROUP, instance-type $INSTANCE_TYPE + AMI $BASE_AMI" - ec2-run-instances --region "$AWS_REGION" \ - --user-data-file "$USER_DATA_FILE" \ - --group "$SECURITY_GROUP" \ - --key "$KEY_NAME" \ - --instance-type "$INSTANCE_TYPE" \ - "$BASE_AMI" >"$LOGFILE_RUN" + echo "Starting EC2 instance in region ${AWS_REGION}, using EC2-Classic with security-group ${SECURITY_GROUP}, instance-type ${INSTANCE_TYPE} + AMI ${BASE_AMI}" + aws ec2 run-instances --region "${AWS_REGION}" \ + --user-data "file://${USER_DATA_FILE}" \ + --group "${SECURITY_GROUP}" \ + --key "${KEY_NAME}" \ + --instance-type "${INSTANCE_TYPE}" \ + --image-id "${BASE_AMI}" >"${LOGFILE_RUN}" fi -INSTANCE_ID="$(awk '/INSTANCE/ {print $2}' "$LOGFILE_RUN")" -if [ -z "$INSTANCE_ID" ] ; then - echo "Could not identify instance ID, exiting." >&2 +INSTANCE_ID="$(jq -r '.Instances[].InstanceId' "${LOGFILE_RUN}")" +if [ -z "${INSTANCE_ID}" ] ; then + echo "Error: Could not identify instance ID, exiting." >&2 exit 1 fi # assign tag so we de-register again the AMI if needed -ec2-create-tags --region "$AWS_REGION" "$INSTANCE_ID" --tag Purpose="$TAG_PURPOSE" +aws ec2 create-tags --region "${AWS_REGION}" --resources "${INSTANCE_ID}" --tag "Key=Purpose,Value=${TAG_PURPOSE}" # assign name (useful for web management console browsing) -ec2-create-tags --region "$AWS_REGION" "$INSTANCE_ID" --tag Name="${AMI_NAME}/${DATE_STRING}/${JOB_NAME}/build_${BUILD_NUMBER}" +[ -z "${JOB_NAME:-}" ] && JOB_NAME="unset" # running outside of Jenkins +[ -z "${BUILD_NUMBER:-}" ] && BUILD_NUMBER="executed-by-$(whoami)" # running outside of Jenkins +aws ec2 create-tags --region "${AWS_REGION}" --resources "${INSTANCE_ID}" --tag "Key=Name,Value=${AMI_NAME}/${DATE_STRING}/${JOB_NAME}/build_${BUILD_NUMBER}" + +echo "Starting instance ID ${INSTANCE_ID} for ngcp version ${NGCP_VERSION}" -STATUS="$(ec2-describe-instances --region ${AWS_REGION} --filter instance-id="$INSTANCE_ID" | awk '/INSTANCE/ {print $5}')" -echo "Starting instance ID $INSTANCE_ID for ngcp version $NGCP_VERSION" +STATUS="$(aws ec2 describe-instances --region ${AWS_REGION} --filter "Name=instance-id,Values=${INSTANCE_ID}" | jq -r '.Reservations[].Instances[].State.Name')" retry=120 # up to 10 minutes -while [ "$STATUS" != "running" ] && [ $retry -ne 0 ] ; do - # NOTE: it's '$5' here, becoming '$6' later iff elastic IP is attached, this ugly parsing will stay until we switch to e.g. awscli - STATUS="$(ec2-describe-instances --region ${AWS_REGION} --filter instance-id="$INSTANCE_ID" | awk '/INSTANCE/ {print $5}')" +while [ "${STATUS}" != "running" ] && [ ${retry} -ne 0 ] ; do + STATUS="$(aws ec2 describe-instances --region ${AWS_REGION} --filter "Name=instance-id,Values=${INSTANCE_ID}" | jq -r '.Reservations[].Instances[].State.Name')" + retry=$((retry - 1)) - echo "Instance ID $INSTANCE_ID not running yet, checking again in 5 seconds ($retry retries left)." + echo "Instance ID ${INSTANCE_ID} not running yet, checking again in 5 seconds (${retry} retries left)." sleep 5 done -if [ "$STATUS" != "running" ] ; then - echo "No further retries left and couldn't bring up system for AMI $BASE_AMI, giving up." >&2 +if [ "${STATUS}" != "running" ] ; then + echo "No further retries left and couldn't bring up system for AMI ${BASE_AMI}, giving up." >&2 bailout 1 fi # assign elastic IP address to instance, # enabling us to enable access to restricted # webserver areas -if [ -n "$ALLOCATION_ID" ] ; then - echo "Associating elastic IP $ELASTIC_IP allocation ID ${ALLOCATION_ID} with instance ID $INSTANCE_ID" - ec2-associate-address --region "${AWS_REGION}" --allocation-id "${ALLOCATION_ID}" --instance "${INSTANCE_ID}" --allow-reassociation -elif [ -n "$ELASTIC_IP" ] ; then - echo "Associating elastic IP $ELASTIC_IP with instance ID $INSTANCE_ID" - ec2-associate-address --region "${AWS_REGION}" "${ELASTIC_IP}" -i "${INSTANCE_ID}" --allow-reassociation +if [ -n "${ALLOCATION_ID}" ] ; then + echo "Associating elastic IP ${ELASTIC_IP} allocation ID ${ALLOCATION_ID} with instance ID ${INSTANCE_ID}" + aws ec2 associate-address --region "${AWS_REGION}" --allocation-id "${ALLOCATION_ID}" --instance "${INSTANCE_ID}" --allow-reassociation +elif [ -n "${ELASTIC_IP}" ] ; then + echo "Associating elastic IP ${ELASTIC_IP} with instance ID ${INSTANCE_ID}" + aws ec2 associate-address --region "${AWS_REGION}" "${ELASTIC_IP}" -i "${INSTANCE_ID}" --allow-reassociation fi -HOSTNAME="$(ec2-describe-instances --region ${AWS_REGION} --filter instance-id="$INSTANCE_ID" | awk '/INSTANCE/ {print $4}')" +HOSTNAME="$(aws ec2 describe-instances --region ${AWS_REGION} --filter "Name=instance-id,Values=${INSTANCE_ID}" | jq -r '.Reservations[].Instances[] | {PublicDnsName} | [.[]] | .[0]')" retry=360 # up to 30 minutes FINISHED_NGCP_CE_INSTALLATION=false @@ -498,24 +524,24 @@ grep_cmd="grep -q 'Installation finished. Thanks for choosing NGCP' /var/log/ngc if "${init_configuration}" ; then grep_cmd="grep -q 'System was successfully configured, now you have the best VoIP software.' /var/log/ngcp-installer.log" fi -while [ "$FINISHED_NGCP_CE_INSTALLATION" != "true" ] && [ $retry -ne 0 ] ; do +while [ "${FINISHED_NGCP_CE_INSTALLATION}" != "true" ] && [ ${retry} -ne 0 ] ; do # shellcheck disable=SC2029 - ssh -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -i "${KEY_FILE}" "admin@$HOSTNAME" \ + ssh -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -i "${KEY_FILE}" "admin@${HOSTNAME}" \ "${grep_cmd}" RC=$? - if [ $RC -eq 0 ] ; then + if [ ${RC} -eq 0 ] ; then FINISHED_NGCP_CE_INSTALLATION=true else - echo "ngcp installation not yet finished, checking again in 5 seconds ($retry retries left)." + echo "ngcp installation not yet finished, checking again in 5 seconds (${retry} retries left)." sleep 5 retry=$((retry - 1)) fi done echo "Trying to retrieve ngcp-installer log files" -scp -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -i "${KEY_FILE}" "admin@$HOSTNAME":/var/log/ngcp-installer\*log ./ || true +scp -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -i "${KEY_FILE}" "admin@${HOSTNAME}":/var/log/ngcp-installer\*log ./ || true -if [ "$FINISHED_NGCP_CE_INSTALLATION" = "true" ] ; then +if [ "${FINISHED_NGCP_CE_INSTALLATION}" = "true" ] ; then echo "Successfully finished ngcp CE installation (version ${NGCP_VERSION})." else echo "No further retries left and couldn't finish ngcp CE installation (version ${NGCP_VERSION}), giving up." >&2 @@ -523,126 +549,130 @@ else fi #Remove temporary crontab -ssh -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -i "${KEY_FILE}" "admin@$HOSTNAME" "sudo crontab -l || sudo crontab -r" +ssh -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -i "${KEY_FILE}" "admin@${HOSTNAME}" "sudo crontab -l || sudo crontab -r" # reboot CE system -if $_opt_skip_reboot ; then +if ${_opt_skip_reboot} ; then echo "*** Skipping system reboot of instance ID ${INSTANCE_ID} as requested via --skip-reboot ***" else - ssh -o "ServerAliveInterval=10" -o "ServerAliveCountMax=1" -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -i "${KEY_FILE}" "admin@$HOSTNAME" "sudo reboot" + ssh -o "ServerAliveInterval=10" -o "ServerAliveCountMax=1" -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -i "${KEY_FILE}" "admin@${HOSTNAME}" "sudo reboot" retry=120 # up to 10 minutes STATUS="" - while [ "$STATUS" != "running" ] && [ $retry -ne 0 ] ; do - STATUS="$(ec2-describe-instances --region ${AWS_REGION} --filter instance-id="$INSTANCE_ID" | awk '/^INSTANCE/ {print $6}')" + while [ "${STATUS}" != "running" ] && [ ${retry} -ne 0 ] ; do + STATUS="$(aws ec2 describe-instances --region ${AWS_REGION} --filter "Name=instance-id,Values=${INSTANCE_ID}" | jq -r '.Reservations[].Instances[].State.Name')" retry=$((retry - 1)) - echo "Instance ID not yet running (status: ${STATUS}), checking again in 5 seconds ($retry retries left)." + echo "Instance ID not yet running (status: ${STATUS}), checking again in 5 seconds (${retry} retries left)." sleep 5 done - if [ "$STATUS" != "running" ] ; then + if [ "${STATUS}" != "running" ] ; then echo "No further retries left and couldn't finish reboot of instance ID ${INSTANCE_ID}, giving up." >&2 bailout 1 fi # we probably have a new hostname after reboot - HOSTNAME="$(ec2-describe-instances --region ${AWS_REGION} --filter instance-id="$INSTANCE_ID" | awk '/INSTANCE/ {print $4}')" + HOSTNAME="$(aws ec2 describe-instances --region ${AWS_REGION} --filter "Name=instance-id,Values=${INSTANCE_ID}" | jq -r '.Reservations[].Instances[] | {PublicDnsName} | [.[]] | .[0]')" # now check for available ssh connection and nginx listening on port 1443 retry=120 # up to 10 minutes STATUS="" - while [ "$STATUS" != "ok" ] && [ $retry -ne 0 ] ; do - ssh -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -i "${KEY_FILE}" "admin@$HOSTNAME" "sudo lsof -i -n -P | grep -q '.*:1443 (LISTEN)'" + while [ "${STATUS}" != "ok" ] && [ ${retry} -ne 0 ] ; do + ssh -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -i "${KEY_FILE}" "admin@${HOSTNAME}" "sudo lsof -i -n -P | grep -q '.*:1443 (LISTEN)'" if [ $? -eq 0 ] ; then STATUS=ok else retry=$((retry - 1)) - echo "Host $HOSTNAME can't be reached via ssh login yet or nginx not listening on port 1443." - echo "Retrying ssh access and checking port 1443 LISTEN state in 5 seconds ($retry retries left)." + echo "Host ${HOSTNAME} can't be reached via ssh login yet or nginx not listening on port 1443." + echo "Retrying ssh access and checking port 1443 LISTEN state in 5 seconds (${retry} retries left)." sleep 5 fi done - if [ "$STATUS" != "ok" ] ; then - echo "Host $HOSTNAME can't be reached via ssh or port 1443 not in state LISTEN, giving up." >&2 + if [ "${STATUS}" != "ok" ] ; then + echo "Host ${HOSTNAME} can't be reached via ssh or port 1443 not in state LISTEN, giving up." >&2 bailout 1 fi fi -remove_ami "$AWS_REGION" "$AMI_NAME" || bailout 1 +remove_ami "${AWS_REGION}" "${AMI_NAME}" || bailout 1 echo "*** Getting rid of all authorized_keys files. ***" -ssh -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -i "${KEY_FILE}" "admin@$HOSTNAME" \ +ssh -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -i "${KEY_FILE}" "admin@${HOSTNAME}" \ 'sudo find / -name authorized_keys -exec rm -f {} \;' LOGFILE_AMI=$(mktemp) -ec2-create-image --region "$AWS_REGION" \ - --name "$AMI_NAME" \ - --description "$AMI_DESCRIPTION" \ - "$INSTANCE_ID" >"$LOGFILE_AMI" +aws ec2 create-image \ + --region "${AWS_REGION}" \ + --name "${AMI_NAME}" \ + --description "${AMI_DESCRIPTION}" \ + --instance-id "${INSTANCE_ID}" \ + > "${LOGFILE_AMI}" + +AMI_ID=$(jq -r '.ImageId' "${LOGFILE_AMI}") -AMI_ID="$(awk '/^IMAGE/ {print $2}' "$LOGFILE_AMI")" +if [ -n "${AMI_ID}" ] ; then + echo "Received AMI ID ${AMI_ID}, now waiting until AMI is available." +else + echo "Error creating AMI :(" >&2 + cat "${LOGFILE_AMI}" >&2 + bailout 1 +fi retry=120 # up to 10 minutes STATUS="" -while [ "$STATUS" != "available" ] && [ $retry -ne 0 ] ; do - STATUS="$(ec2-describe-images --region ${AWS_REGION} "$AMI_ID" | awk '/^IMAGE/ {print $5}')" +while [ "${STATUS}" != "available" ] && [ ${retry} -ne 0 ] ; do + STATUS="$(aws ec2 describe-images --region "${AWS_REGION}" --image-ids "${AMI_ID}" | jq -r '.Images[].State')" - if [ "$STATUS" = "failed" ] ; then - echo "Fatal error while creating AMI ID $AMI_ID (status: ${STATUS}), exiting." >&2 + if [ "${STATUS}" = "failed" ] ; then + echo "Fatal error while creating AMI ID ${AMI_ID} (status: ${STATUS}), exiting." >&2 bailout 1 fi retry=$((retry - 1)) - echo "AMI ID $AMI_ID not yet finished (status: ${STATUS}), checking again in 5 seconds ($retry retries left)." + echo "AMI ID ${AMI_ID} not yet finished (status: ${STATUS}), checking again in 5 seconds (${retry} retries left)." sleep 5 done -if [ "$STATUS" != "available" ] ; then +if [ "${STATUS}" != "available" ] ; then echo "No further retries left and couldn't finish creation of AMI ID ${AMI_ID} for instance ID ${INSTANCE_ID}, giving up." >&2 bailout 1 fi -if [ -n "$AMI_ID" ] ; then - echo "Successfully created AMI with ID $AMI_ID" -else - echo "Error creating AMI :(" >&2 - cat "$LOGFILE_AMI" >&2 - bailout 1 -fi - -if $_opt_copy_to_all_regions ; then - copy_ami "$AWS_REGION" "$AMI_ID" "$AMI_NAME" "$AMI_DESCRIPTION" || bailout 1 -fi +echo "Successfully created AMI with ID ${AMI_ID}" -if $_opt_public ; then +if ${_opt_public} ; then echo "Marking AMI as public" - ec2-modify-image-attribute --region "$AWS_REGION" "$AMI_ID" --launch-permission -a all + aws ec2 modify-image-attribute --region "${AWS_REGION}" --image-id "${AMI_ID}" --launch-permission "Add=[{Group=all}]" if [ $? -ne 0 ] ; then - echo "Noticed problem when trying to mark AMI with ID $AMI_ID as public." >&2 + echo "Noticed problem when trying to mark AMI with ID ${AMI_ID} as public." >&2 bailout 1 fi fi +if ${_opt_copy_to_all_regions} ; then + copy_ami "${AWS_REGION}" "${AMI_ID}" "${AMI_NAME}" "${AMI_DESCRIPTION}" || bailout 1 +fi + echo "*** Debugging information ***" echo "--- user-data-file ---" -cat "$USER_DATA_FILE" +cat "${USER_DATA_FILE}" echo "--- log file for instance run ---" -cat "$LOGFILE_RUN" +cat "${LOGFILE_RUN}" echo "--- log file for AMI creation ---" -cat "$LOGFILE_AMI" +cat "${LOGFILE_AMI}" echo "*** End of Debugging information ***" echo echo "*** Status report for AMI *** -AWS region: $AWS_REGION -AMI ID: $AMI_ID -AMI name: $AMI_NAME -AMI description: $AMI_DESCRIPTION -AMI public: $_opt_public -Instance type: $INSTANCE_TYPE -NGCP version: $NGCP_VERSION +AWS region: ${AWS_REGION} +AMI ID: ${AMI_ID} +AMI name: ${AMI_NAME} +AMI description: ${AMI_DESCRIPTION} +AMI public: ${_opt_public} +Instance type: ${INSTANCE_TYPE} +NGCP version: ${NGCP_VERSION} *** End of Status report ***" if [ -r ec2_report_amis.txt ] ; then @@ -651,8 +681,8 @@ if [ -r ec2_report_amis.txt ] ; then fi # Jenkins setup specific -TESTHOST="$HOSTNAME" -case "$NGCP_RELEASE" in +TESTHOST="${HOSTNAME}" +case "${NGCP_RELEASE}" in latest) VMVERSION="trunk" ;; @@ -664,4 +694,4 @@ esac generate_ec2_report -rm -f "$USER_DATA_FILE" "$LOGFILE_RUN" "$LOGFILE_AMI" +rm -f "${USER_DATA_FILE}" "${LOGFILE_RUN}" "${LOGFILE_AMI}" diff --git a/ec2-publish-ami b/ec2-publish-ami index 6c884c3..9b37b70 100755 --- a/ec2-publish-ami +++ b/ec2-publish-ami @@ -1,15 +1,7 @@ #!/bin/bash -export EC2_HOME="/usr/share/ec2/ec2-api-tools" -export JAVA_HOME="$(dirname $(dirname $(readlink /etc/alternatives/java)))" -export PATH="$PATH:${EC2_HOME}/bin" export TZ="UTC" -# retrieve AWS_* environment variables -if [ -r "$HOME/.ec2-create-ngcp" ] ; then - source "$HOME/.ec2-create-ngcp" -fi - usage() { echo "$0 - script to mark a specified EC2 AMI as public @@ -28,14 +20,12 @@ Supported OPTIONS: CMDLINE_OPTS=ami:,help,region: -_opt_temp=$(getopt --name $0 -o +bch --long $CMDLINE_OPTS -- "$@") +_opt_temp=$(getopt --name "$0" -o +bch --long ${CMDLINE_OPTS} -- "$@") if [ $? -ne 0 ]; then echo "Try '$0 --help' for more information." >& 2 exit 1 fi -eval set -- "$_opt_temp" - -_opt_public=false +eval set -- "${_opt_temp}" while :; do case "$1" in @@ -60,42 +50,32 @@ while :; do done # if unset set sane defaults -[ -n "$AWS_REGION" ] || AWS_REGION="eu-west-1" - -if [ -z "$AWS_ACCESS_KEY" ] ; then - echo "AWS_ACCESS_KEY is unset, can not continue." >&2 - exit 1 -fi - -if [ -z "$AWS_SECRET_KEY" ] ; then - echo "AWS_SECRET_KEY is unset, can not continue." >&2 - exit 1 -fi +[ -n "${AWS_REGION}" ] || AWS_REGION="eu-west-1" check4progs(){ local RC='' - for arg in $* ; do - which $arg >/dev/null 2>&1 || RC="$arg" + for arg in "$@" ; do + which "${arg}" >/dev/null 2>&1 || RC="${arg}" done - if [ -n "$RC" ] ; then - echo "$RC not found/executable" >&2 + if [ -n "${RC}" ] ; then + echo "${RC} not found/executable" >&2 return 1 fi } -if ! check4progs ec2-modify-image-attribute ; then - echo "Required tools not found, forgot to install ec2-api-tools?" >&2 +if ! check4progs aws ; then + echo "Required tool aws not found, forgot to install awscli?" >&2 exit 1 fi -if [ -z "$AMI_ID" ] ; then +if [ -z "${AMI_ID}" ] ; then usage >&2 exit 1 fi -echo "Marking AMI $AMI_ID as public" -ec2-modify-image-attribute --region "$AWS_REGION" "$AMI_ID" --launch-permission -a all +echo "Marking AMI ${AMI_ID} as public" +aws ec2 modify-image-attribute --region "${AWS_REGION}" --image-id "${AMI_ID}" --launch-permission "Add=[{Group=all}]" if [ $? -ne 0 ] ; then - echo "Noticed problem when trying to mark AMI with ID $AMI_ID as public." >&2 + echo "Noticed problem when trying to mark AMI with ID ${AMI_ID} as public." >&2 exit 1 fi diff --git a/ec2-stop-instance b/ec2-stop-instance index d35b363..abaa6be 100755 --- a/ec2-stop-instance +++ b/ec2-stop-instance @@ -1,15 +1,7 @@ #!/bin/bash -export EC2_HOME="/usr/share/ec2/ec2-api-tools" -export JAVA_HOME="$(dirname $(dirname $(readlink /etc/alternatives/java)))" -export PATH="$PATH:${EC2_HOME}/bin" export TZ="UTC" -# retrieve AWS_* environment variables -if [ -r "$HOME/.ec2-create-ngcp" ] ; then - source "$HOME/.ec2-create-ngcp" -fi - usage() { echo "$0 - script to stop the specified EC2 instance ID @@ -29,14 +21,13 @@ Supported OPTIONS: CMDLINE_OPTS=help,instance-id:,region:,terminate -_opt_temp=$(getopt --name $0 -o +bch --long $CMDLINE_OPTS -- "$@") +_opt_temp=$(getopt --name "$0" -o +bch --long ${CMDLINE_OPTS} -- "$@") if [ $? -ne 0 ]; then echo "Try '$0 --help' for more information." >& 2 exit 1 fi -eval set -- "$_opt_temp" +eval set -- "${_opt_temp}" -_opt_public=false _opt_terminate=false while :; do @@ -65,51 +56,41 @@ while :; do done # if unset set sane defaults -[ -n "$AWS_REGION" ] || AWS_REGION="eu-west-1" - -if [ -z "$AWS_ACCESS_KEY" ] ; then - echo "AWS_ACCESS_KEY is unset, can not continue." >&2 - exit 1 -fi - -if [ -z "$AWS_SECRET_KEY" ] ; then - echo "AWS_SECRET_KEY is unset, can not continue." >&2 - exit 1 -fi +[ -n "${AWS_REGION}" ] || AWS_REGION="eu-west-1" check4progs(){ local RC='' - for arg in $* ; do - which $arg >/dev/null 2>&1 || RC="$arg" + for arg in "$@" ; do + which "${arg}" >/dev/null 2>&1 || RC="${arg}" done - if [ -n "$RC" ] ; then - echo "$RC not found/executable" >&2 + if [ -n "${RC}" ] ; then + echo "${RC} not found/executable" >&2 return 1 fi } -if ! check4progs ec2-stop-instances ; then - echo "Required tools not found, forgot to install ec2-api-tools?" >&2 +if ! check4progs aws ; then + echo "Required tool aws not found, forgot to install awscli?" >&2 exit 1 fi -if [ -z "$INSTANCE_ID" ] ; then +if [ -z "${INSTANCE_ID}" ] ; then usage >&2 exit 1 fi -if $_opt_terminate ; then - echo "Terminating instance ID $INSTANCE_ID (as requested via --terminate)" - ec2-terminate-instances --region "$AWS_REGION" "$INSTANCE_ID" +if ${_opt_terminate} ; then + echo "Terminating instance ID ${INSTANCE_ID} (as requested via --terminate)" + aws ec2 terminate-instances --region "${AWS_REGION}" --instance-ids "${INSTANCE_ID}" if [ $? -ne 0 ] ; then - echo "Noticed problem when trying to terminate instance with ID $INSTANCE_ID" >&2 + echo "Noticed problem when trying to terminate instance with ID ${INSTANCE_ID}" >&2 exit 1 fi else - echo "Stopping Instance ID $INSTANCE_ID" - ec2-stop-instances --region "$AWS_REGION" "$INSTANCE_ID" + echo "Stopping Instance ID ${INSTANCE_ID}" + aws ec2 stop-instances --region "${AWS_REGION}" --instance-ids "${INSTANCE_ID}" if [ $? -ne 0 ] ; then - echo "Noticed problem when trying to stop instance with ID $INSTANCE_ID" >&2 + echo "Noticed problem when trying to stop instance with ID ${INSTANCE_ID}" >&2 exit 1 fi fi