Skip to content

Instantly share code, notes, and snippets.

@stevenpetryk
Last active August 3, 2020 20:07
Show Gist options
  • Save stevenpetryk/37e5eef94dcf6dfff62bc0059d4f5b98 to your computer and use it in GitHub Desktop.
Save stevenpetryk/37e5eef94dcf6dfff62bc0059d4f5b98 to your computer and use it in GitHub Desktop.
A bitbar app that starts and stops a DO droplet
Host workspace
User steven
ForwardAgent yes
ControlPersist 300
HostName 64.225.89.72
StrictHostKeyChecking no
UserKnownHostsFile=/dev/null
#!/usr/bin/env bash
set -euo pipefail
# README
#
# This script creates a BitBar application that lets you manage a Droplet
# suitable for daily development use. When you're done working, it will power
# down, snapshot, and destroy the droplet to save you money.
#
# When you're ready to work again, it will create a new Droplet from the
# snapshot and assign a floating IP to it.
#
# Credit to @andrewhamon for the idea. I just made it a BitBar application.
# See https://github.com/andrewhamon/pcmd
################################################################################
# Configuration
# ——————————————————————————————————————————————————————————————————————————————
# You can duplicate this file and change the $NAME variable to have multiple
# workspaces. Make sure you make all $NAMEs unique. Beware DigitalOcean's rate
# limits when doing this.
################################################################################
# The name given in the Mac menu bar
NICKNAME="Workspace"
# The DigitalOcean Droplet size, image and region
SIZE=s-6vcpu-16gb
IMAGE=ubuntu-18-04-x64
REGION=sfo3
# The name given to the actual Droplet and the prefix given to every snapshot.
# If you have multiple copies of this script, you should change this to a unique
# name in each one.
NAME=workspace.$IMAGE
# The IP of a floating IP you MUST set up before using this script. It will give
# your Droplet a stable IP address to SSH into. It does cost a few dollars a
# month when it's not assigned to a Droplet.
FLOATING_IP=64.225.89.72
################################################################################
# Code (no need to edit)
################################################################################
export PATH="/usr/local/bin:$PATH"
if ! command -v jq &> /dev/null; then
echo "Ensure jq is installed."
exit
fi
if ! command -v doctl &> /dev/null; then
echo "Ensure doctl is installed and authenticated."
exit
fi
log(){
echo $@ >&2
}
# Run commands with stdout and stderr redirected to /dev/null
shush(){
$@ &>/dev/null
}
fetch_ssh_keys(){
log "Fetching account SSH keys"
# Join ids with a comma, and delete the trailing comma
doctl compute ssh-key list --format ID --no-header | tr '\n' ',' | sed 's/,$//'
}
# Find the most recent snapshot that includes the VPS name as the prefix of the
# snapshot name
fetch_latest_snapshot(){
local prefix=$1
# log "Checking for existing snapshot"
doctl compute snapshot list --format Name,ID --no-header | grep $prefix | sort | tail -1 | awk '{print $NF}'
}
fetch_running_droplet(){
local prefix=$1
log "Checking for existing droplet"
doctl compute droplet list --format Name,ID --no-header | grep $prefix | tail -1 | awk '{print $NF}'
}
create_and_wait(){
local image=$1
local ssh_keys=$(fetch_ssh_keys)
log "Creating instance $NAME"
doctl compute droplet create $NAME \
--image $image \
--region $REGION \
--size $SIZE \
--ssh-keys $ssh_keys \
--enable-monitoring \
--enable-private-networking \
--enable-ipv6 \
--format ID,PublicIPv4 \
--no-header \
--wait
}
assign_floating_ip_and_wait(){
local instance_id=$1
log "Assigning floating IP $FLOATING_IP to $instance_id"
doctl compute floating-ip-action assign $FLOATING_IP $instance_id
}
shutdown_and_wait(){
local instance_id=$1
log "Shutting down"
shush doctl compute droplet-action shutdown $instance_id --wait
}
snapshot_and_wait(){
local old_snapshot_id=$(fetch_latest_snapshot $NAME)
local instance_id=$1
local snapshot_name=$NAME.$(date -u +"%Y-%m-%dT%H:%M:%SZ")
log "Taking snapshot"
shush doctl compute droplet-action snapshot $instance_id --snapshot-name $snapshot_name --wait
}
delete(){
local instance_id=$1
log "Deleting instance"
shush doctl compute droplet delete --force $instance_id
}
start(){
local latest_snapshot_id=$(fetch_latest_snapshot $NAME)
local create_result
case $latest_snapshot_id in
(*[![:space:]]*)
log "Snapshot found: $latest_snapshot_id"
create_and_wait $latest_snapshot_id
;;
(*)
log "Snapshot not found"
create_and_wait $IMAGE
;;
esac
local instance_id=$(fetch_running_droplet $NAME)
assign_floating_ip_and_wait $instance_id
}
stop(){
local instance_id=$(fetch_running_droplet $NAME)
shutdown_and_wait $instance_id
snapshot_and_wait $instance_id
delete $instance_id
}
fetch_status() {
local prefix=$1
local status=$(doctl compute droplet list --format Name,Status --no-header | grep $prefix | tail -1 | awk '{print $NF}')
if [ -z "$status" ]; then
echo "🔴"
else
echo "🟢 $status"
fi
}
if [ $# -eq 0 ]; then
status=$(fetch_status $NAME)
amount=$(doctl invoice summary preview --format Amount --no-header)
echo "$NICKNAME $status"
echo "---"
echo "Usage: \$$amount"
if [ "$status" = "🔴" ]; then
echo "Start $NICKNAME | bash='$0 start'"
else
echo "Stop $NICKNAME | bash='$0 stop'"
fi
else
case $1 in
"start")
start
;;
"stop")
stop
;;
"cleanup")
cleanup_old_snapshots
esac
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment