Skip to content

Instantly share code, notes, and snippets.

@aitor

aitor/heroku.sh

Created Aug 13, 2019
Embed
What would you like to do?
Refresh demo/staging data in Heroku (cf. http://covidence.engineering/automatically-refresh-staging-db/)
#!/usr/bin/env bash
set -ex
# Pass in Heroku API token
token=$1
# We run this script in a stateless Docker image, so to make the script a bit
# cleaner, we just write out the credentials to a file that the Heroku Toolbelt
# knows to read.
if ! grep -lq api.heroku.com ~/.netrc 2>/dev/null; then
echo "machine api.heroku.com" > ~/.netrc
echo " login dev+heroku@covidence.org" >> ~/.netrc
echo " password $token" >> ~/.netrc
chmod 0600 ~/.netrc
fi
# Helper function for calling Heroku API
function hapi {
curl \
--fail \
--silent \
--netrc \
--header 'Accept: application/vnd.heroku+json; version=3' \
https://api.heroku.com$*
}
# Routine to wait for DB to be caught up, then promote it and de-provision the
# old instance.
function promote {
app=$1
heroku pg:wait --wait-interval 10 -a $app
heroku pg:unfollow -a $app --confirm $app $app::NEW_DATABASE || true
heroku addons:attach -a $app $app::DATABASE --as OLD_DATABASE
heroku pg:promote -a $app NEW_DATABASE
heroku addons:detach -a $app NEW_DATABASE
heroku addons:destroy -a $app --confirm $app OLD_DATABASE
heroku run:detached -a $app rails runner 'Ops::Staging::Search.massacre!'
}
# Routine to create a new DB with a deterministic name so that we can more
# easily promote it later.
#
# There is also some logic here to recover from a build which got interrupted
# mid-way so that we don't accumulate orphaned DBs that we have to pay for.
function create {
app=$1
plan=$2
shift 2
existing=$(hapi /addons/$app::DATABASE | jq -Mr .name)
new=$(hapi /addons/$app::NEW_DATABASE | jq -Mr .name || true)
if [ -z "$new" ]; then
heroku addons:create \
-a $app heroku-postgresql:$plan \
--as NEW_DATABASE \
$*
else
if [ "$new" == "$existing" ]; then
heroku addons:detach -a $app NEW_DATABASE
create $app $plan
else
true # already got a new one in waiting
fi
fi
}
# Takes a snapshot and copies it. This takes longer, breaks the destination
# DB until it has completed, so is only used for demo to demo-staging, which
# use Hobby DBs which cannot be followed/forked.
#
# One benefit of this which might be worth applying to staging/dev is that the
# destination data sizes (tables+indexes) are much smaller, which suits the
# smaller instance sizes used there.
function copy {
src=$1
dst=$2
heroku pg:copy $src::DATABASE NEW_DATABASE \
--wait-interval 10 \
--app $dst \
--confirm $dst
}
# Create new DBs and trigger loading of data
create covidence-dev premium-0 --fork covidence-production::DATABASE --fast
create covidence-staging premium-2 --fork covidence-production::DATABASE --fast
create covidence-demo-staging hobby-basic
copy covidence-demo covidence-demo-staging
# Promote DBs (these steps block until each DB to be ready for promotion)
promote covidence-dev
promote covidence-staging
promote covidence-demo-staging
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment