Skip to content

Instantly share code, notes, and snippets.

@urfolomeus
Created August 4, 2012 15:12
Show Gist options
  • Save urfolomeus/3258292 to your computer and use it in GitHub Desktop.
Save urfolomeus/3258292 to your computer and use it in GitHub Desktop.
Quick script to migrate heroku apps to the new free individual postgres DBs
## README
# This is a quick script I hacked out to migrate all my heroku apps
# to the new free individual postgres DBs. To use it:
# - install the heroku gem if you don't already have it
# - set the value of IGNORE_OTHERS_APPS to true if you only want to
# run the script against apps you've created yourself
# - add any apps you want to ignore because they don't use PostgreSQL
# (or for any other reason) to the IGNORE_LIST
## CAVEAT!!
# - USE AT YOUR OWN RISK!! This works for me, but it may not work for you!
# - I don't remove the SHARED_DATABASE at the end of the process in
# case you want to rollback, so you'll need to delete it yourself once
# you're happy everything worked
# heroku addons:remove shared-database
# - if everything goes south you can just try again, but you'll have to make
# sure that you set the shared database back to be primary by running
# heroku pg:promote SHARED_DATABASE --app <app_name>
## N.B.
# If you do this before Aug 9th then you get:
# - an extra 4000 lines for your DB if on the dev plan
# - $20 credit if on basic or production plan
#
# More info at: https://devcenter.heroku.com/articles/migrating-from-shared-database-to-heroku-postgres
## OPTIONS
IGNORE_OTHERS_APPS = true
IGNORE_LIST = [ ]
class HerokuDBMigrator
def initialize(app_name)
@app_name = app_name
end
def run
raise "No app_name set" if @app_name.nil? or @app_name == ""
maintenance :on
add_required_addons
transfer
rescue Exception => e
p e
ensure
maintenance :off
end
private
def maintenance(state)
heroku "maintenance:#{state}"
puts "maintenance #{state}"
end
def add_required_addons
add "heroku-postgresql:dev"
add "pgbackups"
end
def add(addon)
unless installed?(addon)
puts "adding #{addon}"
heroku "addons:add #{addon}"
end
end
def get_addons
heroku('addons').split(/\n/).map{|a| a.split(' => ').first}.compact
end
def installed?(addon)
@addons ||= get_addons
not @addons.select {|a| a.include?(addon)}.empty?
end
def transfer
backup_existing_db
setup_new_db
end
def backup_existing_db
puts "backing up existing DB"
heroku "pgbackups:capture --expire"
end
def setup_new_db
db_name = heroku("config").match(/(HEROKU_POSTGRESQL_.*)_URL/)[1]
puts "setting up #{db_name}"
heroku "pgbackups:restore #{db_name} --confirm #{@app_name}"
puts "making #{db_name} primary"
heroku "pg:promote #{db_name}"
end
def heroku(command)
`heroku #{command} --app #{@app_name}`
end
end
apps = `heroku apps`.split(/\n/)
apps.reject! {|app| app =~ /\s+/} if IGNORE_OTHERS_APPS
apps -= IGNORE_LIST
apps.each do |app|
puts "\nTransferring app: #{app}"
HerokuDBMigrator.new(app).run
puts
end
@urfolomeus
Copy link
Author

Thanks for the feedback. Will take a look tomorrow. That error will get thrown if 'heroku addins' returns nil I think. Will confirm ramorra though ;)

@urfolomeus
Copy link
Author

I can't figure out what output Heroku is returning to heroku addons that's causing it to barf. My specs pass no matter what I chuck at it. I suspect it's returning an error though. Can you run heroku addons from the command line and let me know what you get please?

In the meantime I've just uniq'd it so that at least the rest of the script will run.

@urfolomeus
Copy link
Author

Oh, just realised that the app name is blank. Perhaps that's the root of the issue?

@adonaldson
Copy link

The only #include? I can see is on line #71. Perhaps the problem is an app has no addons?

Unscientifically derived guess from the sofa!

@urfolomeus
Copy link
Author

Yeah I tested that one as it seemed most obvious culprit (although I'd have thought that no add-ons in heroku would just return an empty array which means the #include? in the select would never get called). I thought that what was happening was that the second split on ' => ' was returning an array with nil as the first element, but I couldn't get any output for 'heroku addons' to produce that result. I figured that by compacting the array (not uniq as I first did in my hungover state :D) would remove any nils and therefore remove errors so I just went with that.

What _was_quite interesting though (for a given vaue of interesting) was that the output you pasted showed that no app_name was being passed in to the HerokuDBMigrator. That would cause all calls to be made with --app at the end with no app. So I reckon I must be incorrectly parsing the error message returned from that. So if you can figure out how it managed to get a nil/blank app name, you'll be on the right path ;)

@urfolomeus
Copy link
Author

Turns out that's exactly what it was. If there you do heroku addons --app you get the usage message returned, which in turn generates an array with nils in it. I've left the compact in, but I've also changed the run method to raise an error before starting if no app_name is set.

Thanks for the heads up :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment