I'm looking at an Rails app that would require establishing a remote SSH session (SSH Tunnel?) from Heroku to a remote Mysql database as part of an background, ActiveRecord session. The goal would be to migrate data at different times via this channel into the app. Connecting to the remote mysql database over the web would not be an option.
Few questions:
Well, I'm now able to answer my own question after digging around for a few days. In short, yes.
class LegacyAccount < ActiveRecord::Base
self.table_name = "account"
def self.test_connection
# net-ssh-gateway gem
gateway = Net::SSH::Gateway.new("ip_address","ssh_user_name",
password: "password",
port: "port if different from 22",
verbose: :debug
)
port = gateway.open('127.0.0.1', 3306, 3307)
establish_connection :legacy_production
result = LegacyAccount.first
puts "Record: #{result.to_yaml}"
gateway.close(port)
gateway.shutdown!
result
end
end
and in your database.yml:
legacy_production:
adapter: "mysql2"
host: "127.0.0.1"
username: "root"
password: "password"
database: "legacydb"
port: 3307
secure_auth: false
I tried @user1322092's approach and got into trouble when multiple clients tried to access the db, since each one would attempt to open a connection (all but the first would get an error).
I created a monstrous solution involving spawning an SSH process on the dyno and using that for all communications. Gory details are in https://stackoverflow.com/a/27361295/558639 . Aside from being really kludgy, it incurs a delay every time a process starts up, whether or not you're accessing the remote database.
So here's a better approach that appears to work well: just trap (and ignore) Errno::EADDRINUSE
errors. Here's the code:
require 'net/ssh/gateway'
class Mole
TUNNEL_HOST = <redacted>
TUNNEL_USER = <redacted>
AWS_HOST = <redacted>
TUNNEL_PORT_NUMBER = 3307
attr_reader :tunnel_gateway, :tunnel_port
# Call this to open an SSH tunnel from the current machine. If the
# the tunnel was already opened (e.g. by another process) , you'll
# get a warning message
def open_tunnel
@tunnel_gateway = Net::SSH::Gateway.new(TUNNEL_HOST, TUNNEL_USER)
begin
@tunnel_port = @tunnel_gateway.open(AWS_HOST, 3306, TUNNEL_PORT_NUMBER)
rescue Errno::EADDRINUSE => e
$stderr.puts("Warning: #{e.class}: #{e.message}")
end
end
# You won't normally call this, because the tunnel is a system wide
# resource; you don't know when other processes want to release it.
def close_tunnel
r = @tunnel_gateway.close(@tunnel_port) if @tunnel_gateway
@tunnel_gateway.shutdown!
r
end
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.