Permalink
Browse files

tests all pass

  • Loading branch information...
jamis committed Apr 28, 2008
1 parent b4bd528 commit 8f0f99565e4ff8a9c3acf9544283be343787be91
View
@@ -19,6 +19,7 @@ desc "Build documentation"
task :doc => [ :rdoc ]
Rake::TestTask.new do |t|
+ t.libs << "test"
t.test_files = Dir["test/**/*_test.rb"]
t.verbose = true
end
@@ -9,7 +9,9 @@ module FileTransfer
# by the current task. If <tt>:mode</tt> is specified it is used to
# set the mode on the file.
def put(data, path, options={})
- upload(StringIO.new(data), path, options)
+ opts = options.dup
+ opts[:permissions] = opts.delete(:mode)
+ upload(StringIO.new(data), path, opts)
end
# Get file remote_path from FIRST server targeted by
@@ -1,6 +1,7 @@
require 'enumerator'
require 'net/ssh/gateway'
require 'capistrano/ssh'
+require 'capistrano/errors'
module Capistrano
class Configuration
@@ -24,8 +25,11 @@ class GatewayConnectionFactory #:nodoc:
def initialize(gateway, options)
Thread.abort_on_exception = true
server = ServerDefinition.new(gateway)
- @gateway = Net::SSH::Gateway.new(server.host, server.user || ServerDefinition.default_user, server.options)
+
@options = options
+ @gateway = SSH.connection_strategy(server, options) do |host, user, connect_options|
+ Net::SSH::Gateway.new(host, user, connect_options)
+ end
end
def connect_to(server)
@@ -177,8 +181,6 @@ def establish_connection_to(server, failures=nil)
def safely_establish_connection_to(server, failures=nil)
sessions[server] ||= connection_factory.connect_to(server)
rescue Exception => err
-puts err
-puts err.backtrace
raise unless failures
failures << { :server => server, :error => err }
end
View
@@ -39,23 +39,38 @@ def self.apply_to(connection, server)
# constructor. Values in +options+ are then merged into it, and any
# connection information in +server+ is added last, so that +server+ info
# takes precedence over +options+, which takes precendence over ssh_options.
- def self.connect(server, options={}, &block)
+ def self.connect(server, options={})
+ connection_strategy(server, options) do |host, user, connection_options|
+ connection = Net::SSH.start(host, user, connection_options)
+ Server.apply_to(connection, server)
+ end
+ end
+
+ # Abstracts the logic for establishing an SSH connection (which includes
+ # testing for connection failures and retrying with a password, and so forth,
+ # mostly made complicated because of the fact that some of these variables
+ # might be lazily evaluated and try to do something like prompt the user,
+ # which should only happen when absolutely necessary.
+ #
+ # This will yield the hostname, username, and a hash of connection options
+ # to the given block, which should return a new connection.
+ def self.connection_strategy(server, options={}, &block)
methods = [ %w(publickey hostbased), %w(password keyboard-interactive) ]
password_value = nil
ssh_options = (server.options[:ssh_options] || {}).merge(options[:ssh_options] || {})
user = server.user || options[:user] || ssh_options[:username] || ServerDefinition.default_user
ssh_options[:port] = server.port || options[:port] || ssh_options[:port] || DEFAULT_PORT
+ ssh_options.delete(:username)
+
begin
connection_options = ssh_options.merge(
:password => password_value,
:auth_methods => ssh_options[:auth_methods] || methods.shift
)
- connection = Net::SSH.start(server.host, user, connection_options, &block)
- Server.apply_to(connection, server)
-
+ yield server.host, user, connection_options
rescue Net::SSH::AuthenticationFailed
raise if methods.empty? || ssh_options[:auth_methods]
password_value = options[:password]
View
@@ -31,9 +31,11 @@ def initialize(direction, from, to, sessions, options={}, &block)
@options = options
@callback = callback
- @transport = options.fetch(:transport, :sftp)
+ @transport = options.fetch(:via, :sftp)
@logger = options.delete(:logger)
-
+
+ @session_map = {}
+
prepare_transfers
end
@@ -91,16 +93,18 @@ def sanitized_to
private
- def prepare_transfers
- @session_map = {}
+ def session_map
+ @session_map
+ end
+ def prepare_transfers
logger.info "#{transport} #{operation} #{from} -> #{to}" if logger
@transfers = sessions.map do |session|
session_from = normalize(from, session)
session_to = normalize(to, session)
- @session_map[session] = case transport
+ session_map[session] = case transport
when :sftp
prepare_sftp_transfer(session_from, session_to, session)
when :scp
@@ -112,24 +116,21 @@ def prepare_transfers
end
def prepare_scp_transfer(from, to, session)
- scp = Net::SCP.new(session)
-
real_callback = callback || Proc.new do |channel, name, sent, total|
logger.trace "[#{channel[:host]}] #{name}" if logger && sent == 0
end
channel = case direction
when :up
- scp.upload(from, to, options, &real_callback)
+ session.scp.upload(from, to, options, &real_callback)
when :down
- scp.download(from, to, options, &real_callback)
+ session.scp.download(from, to, options, &real_callback)
else
raise ArgumentError, "unsupported transfer direction: #{direction.inspect}"
end
- channel[:server] = session.xserver
- channel[:host] = session.xserver.host
- channel[:channel] = channel
+ channel[:server] = session.xserver
+ channel[:host] = session.xserver.host
return channel
end
@@ -138,7 +139,7 @@ class SFTPTransferWrapper
attr_reader :operation
def initialize(session, &callback)
- Net::SFTP::Session.new(session) do |sftp|
+ session.sftp(false).connect do |sftp|
@operation = callback.call(sftp)
end
end
@@ -170,15 +171,12 @@ def prepare_sftp_transfer(from, to, session)
elsif event == :finish
logger.trace "[#{op[:host]}] done"
end
-
- op[:channel].close if event == :finish
end
opts = options.dup
opts[:properties] = (opts[:properties] || {}).merge(
:server => session.xserver,
- :host => session.xserver.host,
- :channel => sftp.channel)
+ :host => session.xserver.host)
case direction
when :up
@@ -205,12 +203,14 @@ def normalize(argument, session)
end
def handle_error(error)
- transfer = @session_map[error.session]
- transfer[:channel].close
+ transfer = session_map[error.session]
transfer[:error] = error
transfer[:failed] = true
- transfer.abort! if transport == :sftp
+ case transport
+ when :sftp then transfer.abort!
+ when :scp then transfer.close
+ end
end
end
end
View
@@ -20,8 +20,7 @@ def test_command_with_windows_newlines_should_be_properly_escaped
end
def test_command_with_pty_should_request_pty_and_register_success_callback
- session = setup_for_extracting_channel_action(:on_success) do |ch|
- ch.expects(:request_pty).with(:want_reply => true)
+ session = setup_for_extracting_channel_action(:request_pty, true) do |ch|
ch.expects(:exec).with(%(sh -c "ls"))
end
Capistrano::Command.new("ls", [session], :pty => true)
@@ -128,7 +127,7 @@ def test_successful_channel_should_send_data_if_data_key_is_present
end
def test_unsuccessful_pty_request_should_close_channel
- session = setup_for_extracting_channel_action(:on_failure) do |ch|
+ session = setup_for_extracting_channel_action(:request_pty, false) do |ch|
ch.expects(:close)
end
Capistrano::Command.new("ls", [session], :pty => true)
@@ -158,7 +157,7 @@ def test_on_extended_data_should_invoke_callback_as_stderr
def test_on_request_should_record_exit_status
data = mock(:read_long => 5)
- session = setup_for_extracting_channel_action(:on_request, "exit-status", nil, data) do |ch|
+ session = setup_for_extracting_channel_action([:on_request, "exit-status"], data) do |ch|
ch.expects(:[]=).with(:status, 5)
end
Capistrano::Command.new("ls", [session])
@@ -172,34 +171,34 @@ def test_on_close_should_set_channel_closed
end
def test_stop_should_close_all_open_channels
- sessions = [mock("session", :open_channel => new_channel(false)),
- mock("session", :open_channel => new_channel(true)),
- mock("session", :open_channel => new_channel(false))]
+ sessions = [mock_session(new_channel(false)),
+ mock_session(new_channel(true)),
+ mock_session(new_channel(false))]
cmd = Capistrano::Command.new("ls", sessions)
cmd.stop!
end
def test_process_should_return_cleanly_if_all_channels_have_zero_exit_status
- sessions = [mock("session", :open_channel => new_channel(true, 0)),
- mock("session", :open_channel => new_channel(true, 0)),
- mock("session", :open_channel => new_channel(true, 0))]
+ sessions = [mock_session(new_channel(true, 0)),
+ mock_session(new_channel(true, 0)),
+ mock_session(new_channel(true, 0))]
cmd = Capistrano::Command.new("ls", sessions)
assert_nothing_raised { cmd.process! }
end
def test_process_should_raise_error_if_any_channel_has_non_zero_exit_status
- sessions = [mock("session", :open_channel => new_channel(true, 0)),
- mock("session", :open_channel => new_channel(true, 0)),
- mock("session", :open_channel => new_channel(true, 1))]
+ sessions = [mock_session(new_channel(true, 0)),
+ mock_session(new_channel(true, 0)),
+ mock_session(new_channel(true, 1))]
cmd = Capistrano::Command.new("ls", sessions)
assert_raises(Capistrano::CommandError) { cmd.process! }
end
def test_command_error_should_include_accessor_with_host_array
- sessions = [mock("session", :open_channel => new_channel(true, 0)),
- mock("session", :open_channel => new_channel(true, 0)),
- mock("session", :open_channel => new_channel(true, 1))]
+ sessions = [mock_session(new_channel(true, 0)),
+ mock_session(new_channel(true, 0)),
+ mock_session(new_channel(true, 1))]
cmd = Capistrano::Command.new("ls", sessions)
begin
@@ -216,43 +215,13 @@ def test_process_should_loop_until_all_channels_are_closed
ch = mock("channel")
returns = [false] * (times-1)
ch.stubs(:[]).with(:closed).returns(*(returns + [true]))
- con = mock("connection")
- con.expects(:process).with(true).times(times-1)
- ch.expects(:connection).times(times-1).returns(con)
ch.expects(:[]).with(:status).returns(0)
ch
end
- sessions = [mock("session", :open_channel => new_channel[5]),
- mock("session", :open_channel => new_channel[10]),
- mock("session", :open_channel => new_channel[7])]
- cmd = Capistrano::Command.new("ls", sessions)
- assert_nothing_raised { cmd.process! }
- end
-
- def test_process_should_ping_all_connections_each_second
- now = Time.now
-
- new_channel = Proc.new do
- ch = mock("channel")
- ch.stubs(:now => now)
- def ch.[](key)
- case key
- when :status then 0
- when :closed then Time.now - now < 1.1 ? false : true
- else raise "unknown key: #{key}"
- end
- end
- con = mock("connection")
- con.stubs(:process)
- con.expects(:ping!)
- ch.stubs(:connection).returns(con)
- ch
- end
-
- sessions = [mock("session", :open_channel => new_channel[]),
- mock("session", :open_channel => new_channel[]),
- mock("session", :open_channel => new_channel[])]
+ sessions = [mock_session(new_channel[5]),
+ mock_session(new_channel[10]),
+ mock_session(new_channel[7])]
cmd = Capistrano::Command.new("ls", sessions)
assert_nothing_raised { cmd.process! }
end
@@ -281,6 +250,13 @@ def test_process_with_unknown_placeholder_should_not_replace_placeholder
private
+ def mock_session(channel=nil)
+ stub('session', :open_channel => channel,
+ :preprocess => true,
+ :postprocess => true,
+ :listeners => {})
+ end
+
def new_channel(closed, status=nil)
ch = mock("channel")
ch.expects(:[]).with(:closed).returns(closed)
@@ -300,7 +276,11 @@ def setup_for_extracting_channel_action(action=nil, *args)
channel.stubs(:[]).with(:server).returns(s)
channel.stubs(:[]).with(:host).returns(s.host)
- channel.expects(action).yields(channel, *args) if action
+
+ if action
+ action = Array(action)
+ channel.expects(action.first).with(*action[1..-1]).yields(channel, *args)
+ end
yield channel if block_given?
Oops, something went wrong.

0 comments on commit 8f0f995

Please sign in to comment.