Created
October 14, 2018 15:42
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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