Skip to content

Instantly share code, notes, and snippets.

@bamarch
Created October 14, 2018 15:42
Show Gist options
  • Save bamarch/958a16b21ff18b93909c85fa53266700 to your computer and use it in GitHub Desktop.
Save bamarch/958a16b21ff18b93909c85fa53266700 to your computer and use it in GitHub Desktop.
bash wrapper around doctl digital ocean cli, can be used to backup & resize a droplet
#!/usr/bin/env bash
# Note if you try to resize to the same size, it just takes a backup.
# One use is to setup as a crontab configured to auto scale down your
# servers at 3am (in-case you forget, saving you money).
# Dependencies:
# doctl (DO cli)
# jq (a great json manipulating cli tool)
# backup-remote-machine (my funky bash script, it's optional)
if [[ $# != 2 ]]; then
echo "usage: <droplet name> <size slug>"
echo " resize-droplet ubuntu-s-1vcpu-1gb-lon1-ansible-01 s-8vcpu-32gb"
echo " resize-droplet ubuntu-s-1vcpu-1gb-lon1-ansible-01 s-1vcpu-1gb"
exit 1
fi
# Config
log_file="/tmp/resize-droplet.log"
ssh_username="bamarch"
main(){
# Gather information
droplet_name="$1"
target_size_slug="$2"
droplet_id=$(fetch-droplet-info-by-name "$droplet_name" "ID")
echo "Found ID $droplet_id for $droplet_name." >> "$log_file" 2>&1
droplet_ipv4=$(fetch-droplet-info-by-name "$droplet_name" "PublicIPv4")
echo "Found IP $droplet_ipv4 for $droplet_name." >> "$log_file" 2>&1
# Backup the droplet locally (could be skipped)
backup-remote-machine "$droplet_ipv4" >> "$log_file" 2>&1
if [[ $? != 0 ]]; then
echo "Backup failure, exiting!" >> "$log_file" 2>&1
fi
# Go no further if the droplet doesn't need resizing
current_size_slug=$(fetch-droplet-info-by-id $droplet_id '.[] .size_slug')
echo "Current size slug: $current_size_slug. Target size slug: $target_size_slug" >> "$log_file" 2>&1
if [[ "$target_size_slug" = "$current_size_slug" ]]; then
echo "Will not proceed further, size slugs match, no resize is required." >> "$log_file" 2>&1
exit 0
fi
# Shutdown the server before resizing
# (requires passwordless sudo - make sure you have a good RSA key, and disable password auth for ssh login)
ssh "${ssh_username}@${droplet_ipv4}" 'sudo shutdown now'
echo "Attempting to switch off droplet..." >> "$log_file" 2>&1
counter=0
until [[ $(fetch-droplet-info-by-name "$droplet_name" "Status") = "off" ]]; do
let "counter++"
if [[ $counter = 20 ]]; then
echo "Timed out waiting for droplet to switch off" >> "$log_file" 2>&1
exit 1
fi
echo "Attempt #$counter: Droplet is $(fetch-droplet-info-by-name "$droplet_name" "Status")" >> "$log_file" 2>&1
echo "Waiting for droplet to switch off..." >> "$log_file" 2>&1
sleep 1
done
echo "Droplet is off." >> "$log_file" 2>&1
# Resize the droplet
echo "Resizing $droplet_name to $target_size_slug" >> "$log_file" 2>&1
resize_action_id=$(droplet-action-resize "$droplet_id" "$target_size_slug")
if [[ -n $resize_action_id ]]; then
echo "Resize action id is $resize_action_id" >> "$log_file" 2>&1
else
echo "Error unable to parse resize action id" >> "$log_file" 2>&1
exit 1
fi
# Wait
echo "Waiting for droplet to resize..." >> "$log_file" 2>&1
counter=0
until [[ $(fetch-droplet-action-resize-status "$droplet_id" "$resize_action_id") = "completed" ]]; do
let "counter++"
if [[ $counter = 3600 ]]; then
echo "Timed out waiting for droplet to be ready" >> "$log_file" 2>&1
exit 1
fi
current_status=$(fetch-droplet-action-resize-status "$droplet_id" "$resize_action_id")
if [[ $current_status != "in-progress" && $current_status != "completed" ]]; then
echo "Unexpected action status: $current_status" >> "$log_file" 2>&1
exit 1
fi
echo "Current resize status is: $current_status"
echo "Loop #$counter: Waiting for droplet to be ready..." >> "$log_file" 2>&1
sleep 1
done
echo "Action $resize_action_id was completed, droplet is resized" >> "$log_file" 2>&1
# Everything went well, power the droplet back on
droplet-power-on "$droplet_id"
}
fetch-droplet-info-by-name() {
# e.g. fetch-droplet-info-by-name $droplet-name 'ID'
# fetch-droplet-info-by-name $droplet-name 'PublicIPv4'
# Whitespace after the name is important to prevent matching substrings
doctl compute droplet list --no-header --format="Name,$2" |\
tee -a "$log_file" |\
grep "^$1 " |\
awk '{print $2}'
}
fetch-droplet-info-by-id() {
# e.g. fetch-droplet-info-by-id $droplet_id '.[] .size_slug'
# fetch-droplet-info-by-id $droplet_id '.[] .networks .v4[] .ip_address'
doctl compute droplet get "$1" --output='json' |\
tee -a "$log_file" |\
jq --raw-output "$2"
}
droplet-action-resize() {
# e.g. droplet-action-resize "$droplet_id" "$target_size_slug"
doctl compute droplet-action resize "$1" --size="$2" --output='json' |\
tee -a "$log_file" |\
jq --raw-output '.[] .id'
}
fetch-droplet-action-resize-status() {
# e.g. fetch-droplet-action-resize-status "$droplet_id" "$resize_action_id"
doctl compute droplet-action get "$1" --action-id "$2" --output='json' |\
jq --raw-output '.[] .status'
}
droplet-power-on() {
# e.g. droplet-power-on "$droplet_id"
doctl compute droplet-action power-on "$1" |\
tee -a "$log_file"
}
#-------------------------------
# Call main method with all args
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment