lib/capistrano/gateway.rb in capistrano-1.4.2 vs lib/capistrano/gateway.rb in capistrano-2.0.0
- old
+ new
@@ -1,99 +1,124 @@
+if RUBY_VERSION == "1.8.6"
+ begin
+ require 'fastthread'
+ rescue LoadError
+ warn "You are running Ruby 1.8.6, which has a bug in its threading implementation."
+ warn "You are liable to encounter deadlocks running Capistrano, unless you install"
+ warn "the fastthread library, which is available as a gem:"
+ warn " gem install fastthread"
+ end
+end
+
require 'thread'
+require 'capistrano/errors'
require 'capistrano/ssh'
+require 'capistrano/server_definition'
Thread.abort_on_exception = true
module Capistrano
# Black magic. It uses threads and Net::SSH to set up a connection to a
# gateway server, through which connections to other servers may be
# tunnelled.
#
- # It is used internally by Actor, but may be useful on its own, as well.
+ # It is used internally by Capistrano, but may be useful on its own, as well.
#
# Usage:
#
- # config = Capistrano::Configuration.new
- # gateway = Capistrano::Gateway.new('gateway.example.com', config)
+ # gateway = Capistrano::Gateway.new(Capistrano::ServerDefinition.new('gateway.example.com'))
#
- # sess1 = gateway.connect_to('hidden.example.com')
- # sess2 = gateway.connect_to('other.example.com')
+ # sess1 = gateway.connect_to(Capistrano::ServerDefinition.new('hidden.example.com'))
+ # sess2 = gateway.connect_to(Capistrano::ServerDefinition.new('other.example.com'))
class Gateway
- # The thread inside which the gateway connection itself is running.
+ # The Thread instance driving the gateway connection.
attr_reader :thread
# The Net::SSH session representing the gateway connection.
attr_reader :session
MAX_PORT = 65535
MIN_PORT = 1024
- def initialize(server, config) #:nodoc:
- @config = config
+ def initialize(server, options={}) #:nodoc:
+ @options = options
@next_port = MAX_PORT
@terminate_thread = false
@port_guard = Mutex.new
mutex = Mutex.new
waiter = ConditionVariable.new
- @thread = Thread.new do
- @config.logger.trace "starting connection to gateway #{server}"
- SSH.connect(server, @config) do |@session|
- @config.logger.trace "gateway connection established"
- mutex.synchronize { waiter.signal }
- @session.loop { !@terminate_thread }
+ mutex.synchronize do
+ @thread = Thread.new do
+ logger.trace "starting connection to gateway `#{server}'" if logger
+ SSH.connect(server, @options) do |@session|
+ logger.trace "gateway connection established" if logger
+ mutex.synchronize { waiter.signal }
+ @session.loop do
+ !@terminate_thread
+ end
+ end
end
- end
- mutex.synchronize { waiter.wait(mutex) }
+ waiter.wait(mutex)
+ end
end
# Shuts down all forwarded connections and terminates the gateway.
def shutdown!
# cancel all active forward channels
- @session.forward.active_locals.each do |lport, host, port|
- @session.forward.cancel_local(lport)
+ session.forward.active_locals.each do |lport, host, port|
+ session.forward.cancel_local(lport)
end
# terminate the gateway thread
@terminate_thread = true
# wait for the gateway thread to stop
- @thread.join
+ thread.join
end
# Connects to the given server by opening a forwarded port from the local
# host to the server, via the gateway, and then opens and returns a new
# Net::SSH connection via that port.
def connect_to(server)
connection = nil
- @config.logger.trace "establishing connection to #{server} via gateway"
+ logger.debug "establishing connection to `#{server}' via gateway" if logger
local_port = next_port
thread = Thread.new do
begin
- user, server_stripped, port = SSH.parse_server(server)
- @config.ssh_options[:username] = user if user
- remote_port = port || 22
- @session.forward.local(local_port, server_stripped, remote_port)
- connection = SSH.connect('127.0.0.1', @config, local_port)
- @config.logger.trace "connection to #{server} via gateway established"
+ local_host = ServerDefinition.new("127.0.0.1", :user => server.user, :port => local_port)
+ session.forward.local(local_port, server.host, server.port || 22)
+ connection = SSH.connect(local_host, @options)
+ connection.xserver = server
+ logger.trace "connected: `#{server}' (via gateway)" if logger
rescue Errno::EADDRINUSE
local_port = next_port
retry
rescue Exception => e
- puts e.class.name
- puts e.backtrace.join("\n")
+ warn "#{e.class}: #{e.message}"
+ warn e.backtrace.join("\n")
end
end
thread.join
- connection or raise "Could not establish connection to #{server}"
+ if connection.nil?
+ error = ConnectionError.new("could not establish connection to `#{server}'")
+ error.hosts = [server]
+ raise error
+ end
+
+ connection
end
private
+
+ def logger
+ @options[:logger]
+ end
def next_port
@port_guard.synchronize do
port = @next_port
@next_port -= 1