Using taps without running a taps server
So, the Ruby world has a nifty thinger for syncing up databases over the interwebs. It’s called Taps, from the superheroes over at Heroku. It’s great — you just run a little Sinatra-based server and then give the database URLs and it handles all of the plumbing.
But, you see, I was none-too-keen on having another long-running Ruby process, not to mention an open port with production database data lumbering around on it, so I thought I’d let you guys in on a little hack we produced internally to let you get all of the fun of taps, but without the taps server.
Basically it starts up the taps server on the remote server, tunnels the transfer over SSH, then sends a ctrl-c to the server to kill it’s done. It’s pull-only very intentionally — I want to push from a development database to a production database about like I want a hole in my head.
#!/usr/bin/env ruby require 'rubygems' require 'active_support/secure_random' require 'net/ssh' SSH_USER = '[sshuser]' SSH_HOST = '[dbhost]' LOCAL_DB = 'mysql://[dbuser]:[dbpass]@localhost/[dbname]' REMOTE_DB = 'mysql://[dbuser]:[dbpass]@localhost/[dbname]' TAPS_USER = ActiveSupport::SecureRandom.hex(16) TAPS_PASS = ActiveSupport::SecureRandom.hex(16) URL = "http://#{TAPS_USER}:#{TAPS_PASS}@localhost:5000" Net::SSH.start(SSH_HOST, SSH_USER, :compression => true) do |ssh| ssh.forward.local(5000, 'localhost', 5000) ready = false channel = ssh.open_channel do |c| c.request_pty c.on_data { |c, data| ready = true if data =~ /port=5000/ } c.exec("taps server #{REMOTE_DB} #{TAPS_USER} #{TAPS_PASS}") end finished = false Thread.new do sleep 0.1 until ready system "taps pull #{LOCAL_DB} #{URL}" finished = true end ssh.loop(0.1) do channel.send_data(Net::SSH::Connection::Term::VINTR) if finished !finished end end |
Substitute in the right values in the constants up at the top and you’ve got a nifty way to securely use taps without leaving a server running.
Leave a comment