class TonnelRunner SSH_TONNEL_COMMAND = "ssh -N -R 0.0.0.0:0:localhost:{{local_port}} -i {{pem}} -o StrictHostKeyChecking=no share@dblink.tk" PEM_FILE = "#{BASE_DIR}/share.pem" attr_reader :local_port, :remote_port, :runner_thread, :runner_pid def initialize(local_port, tmp_dir:, verbose: true) @local_port = local_port.to_i @verbose = verbose @tmp_dir = tmp_dir end def run ssh_command = SSH_TONNEL_COMMAND.gsub('{{local_port}}', @local_port.to_s) puts "Copying pem file to tmp folder" if @verbose tmp_pem = @tmp_dir + '/share.pem' File.open(tmp_pem, 'w') do |file| file.write(File.read(PEM_FILE)) end File.chmod(0600, tmp_pem) ssh_command.gsub!('{{pem}}', tmp_pem) if @verbose puts "EXEC #{ssh_command}" end Open3.popen3(ssh_command) {|stdin, stdout, stderr, wait_thr| @runner_pid = wait_thr.pid # pid of the started process. #p "process_started: #{pid}" Thread.new do begin while line = stdout.gets puts "STDOUT: #{line}" if @verbose end rescue Exception => error puts error.message puts error.backtrace end end Thread.new do begin while line = stderr.gets #puts "STDERR: #{line}" if line =~ /Allocated port/ @remote_port = line.match(/Allocated port (\d+)\s/)[1].to_i #puts "Remote port: #{@remote_port}" end end rescue Exception => error puts error.message puts error.backtrace end end exit_status = wait_thr.value # Process::Status object returned. puts "SSH tonnel: process stoped: #{exit_status}" if @verbose } self end def run_async @runner_thread = Thread.new do begin run rescue Exception => error puts error.message puts error.backtrace end end self end def stop_async if @runner_pid && System.process_alive?(@runner_pid, verbose: @verbose) System.stop_process(@runner_pid) end end def wait_remote_port_allocated while @remote_port.nil? #print '.' sleep 0.1 end @remote_port end end