summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJamis Buck <jamis@37signals.com>2008-04-08 16:45:44 -0600
committerJamis Buck <jamis@37signals.com>2008-04-08 16:45:44 -0600
commit793696dbcf7ac3c763b113f611a56052493f318b (patch)
tree2b80cc27f393be1325f3cf292f27e4e3418594c7
parent73e7a6164730051cca2555ed70b0c34935049514 (diff)
downloadnet-ssh-multi-793696dbcf7ac3c763b113f611a56052493f318b.tar.gz
use an abbreviated session definition syntax (borrowing further from Capistrano)
-rw-r--r--README.rdoc8
-rw-r--r--lib/net/ssh/multi.rb8
-rw-r--r--lib/net/ssh/multi/server.rb39
-rw-r--r--lib/net/ssh/multi/session.rb25
-rw-r--r--test/server_test.rb94
-rw-r--r--test/session_actions_test.rb16
-rw-r--r--test/session_test.rb42
7 files changed, 137 insertions, 95 deletions
diff --git a/README.rdoc b/README.rdoc
index 3a90d57..d80580b 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -24,13 +24,13 @@ In a nutshell:
session.via 'gateway', 'gateway-user'
# define the servers we want to use
- session.use 'host1', 'user1'
- session.use 'host2', 'user2'
+ session.use 'user1@host1'
+ session.use 'user2@host2'
# define servers in groups for more granular access
session.group :app do
- session.use 'app1', 'user'
- session.use 'app2', 'user'
+ session.use 'user@app1'
+ session.use 'user@app2'
end
# execute commands on all servers
diff --git a/lib/net/ssh/multi.rb b/lib/net/ssh/multi.rb
index 8f393df..1664d0e 100644
--- a/lib/net/ssh/multi.rb
+++ b/lib/net/ssh/multi.rb
@@ -19,13 +19,13 @@ module Net; module SSH
# session.via 'gateway', 'gateway-user'
#
# # define the servers we want to use
- # session.use 'host1', 'user1'
- # session.use 'host2', 'user2'
+ # session.use 'user1@host1'
+ # session.use 'user2@host2'
#
# # define servers in groups for more granular access
# session.group :app do
- # session.use 'app1', 'user'
- # session.use 'app2', 'user'
+ # session.use 'user@app1'
+ # session.use 'user@app2'
# end
#
# # execute commands on all servers
diff --git a/lib/net/ssh/multi/server.rb b/lib/net/ssh/multi/server.rb
index 3354b0a..a9d01aa 100644
--- a/lib/net/ssh/multi/server.rb
+++ b/lib/net/ssh/multi/server.rb
@@ -6,13 +6,15 @@ module Net; module SSH; module Multi
# need to instantiate one of these directly: instead, you should use
# Net::SSH::Multi::Session#use.
class Server
+ include Comparable
+
# The Net::SSH::Multi::Session instance that manages this server instance.
attr_reader :master
# The host name (or IP address) of the server to connect to.
attr_reader :host
- # The user name to use when connecting to this server.
+ # The user name to use when logging into the server.
attr_reader :user
# The Hash of additional options to pass to Net::SSH when connecting
@@ -26,15 +28,31 @@ module Net; module SSH; module Multi
# Creates a new Server instance with the given connection information. The
# +master+ argument must be a reference to the Net::SSH::Multi::Session
# instance that will manage this server reference. The +options+ hash must
- # conform to the options described for Net::SSH::start, with one addition:
+ # conform to the options described for Net::SSH::start, with two additions:
#
# * :via => a Net::SSH::Gateway instance to use when establishing a
# connection to this server.
- def initialize(master, host, user, options={})
+ # * :user => the name of the user to use when logging into this server.
+ #
+ # The +host+ argument may include the username and port number, in which
+ # case those values take precedence over similar values given in the +options+:
+ #
+ # server = Net::SSH::Multi::Server.new(session, 'user@host:1234')
+ # puts server.user #-> user
+ # puts server.port #-> 1234
+ def initialize(master, host, options={})
@master = master
- @host = host
- @user = user
@options = options.dup
+
+ @user, @host, port = host.match(/^(?:([^;,:=]+)@|)(.*?)(?::(\d+)|)$/)[1,3]
+
+ user_opt, port_opt = @options.delete(:user), @options.delete(:port)
+
+ @user = @user || user_opt || master.default_user
+ port ||= port_opt
+
+ @options[:port] = port.to_i if port
+
@gateway = @options.delete(:via)
@failed = false
end
@@ -51,15 +69,12 @@ module Net; module SSH; module Multi
options[:port] || 22
end
- # Compares the given +server+ to this instance, and returns true if they
- # have the same host, user, and port.
- def eql?(server)
- host == server.host &&
- user == server.user &&
- port == server.port
+ # Gives server definitions a sort order, and allows comparison.
+ def <=>(server)
+ [host, port, user] <=> [server.host, server.port, server.user]
end
- alias :== :eql?
+ alias :eql? :==
# Generates a +Fixnum+ hash value for this object. This function has the
# property that +a.eql?(b)+ implies +a.hash == b.hash+. The
diff --git a/lib/net/ssh/multi/session.rb b/lib/net/ssh/multi/session.rb
index 4c6e1a0..c7462d6 100644
--- a/lib/net/ssh/multi/session.rb
+++ b/lib/net/ssh/multi/session.rb
@@ -20,13 +20,13 @@ module Net; module SSH; module Multi
# session.via 'gateway', 'gateway-user'
#
# # define the servers we want to use
- # session.use 'host1', 'user1'
- # session.use 'host2', 'user2'
+ # session.use 'user1@host1'
+ # session.use 'user2@host2'
#
# # define servers in groups for more granular access
# session.group :app do
- # session.use 'app1', 'user'
- # session.use 'app2', 'user'
+ # session.use 'user@app1'
+ # session.use 'user@app2'
# end
#
# # execute commands on all servers
@@ -65,6 +65,12 @@ module Net; module SSH; module Multi
# :warn if connection errors should cause a warning.
attr_accessor :on_error
+ # The default user name to use when connecting to a server. If a user name
+ # is not given for a particular server, this value will be used. It defaults
+ # to ENV['USER'] || ENV['USERNAME'], or "unknown" if neither of those are
+ # set.
+ attr_accessor :default_user
+
# The number of connections that are currently open.
attr_reader :open_connections #:nodoc:
@@ -92,6 +98,7 @@ module Net; module SSH; module Multi
@open_groups = []
@connect_threads = []
@on_error = :fail
+ @default_user = ENV['USER'] || ENV['USERNAME'] || "unknown"
@open_connections = 0
@pending_sessions = []
@@ -171,11 +178,11 @@ module Net; module SSH; module Multi
# a different Net::SSH::Gateway instance (or +nil+) with the :via key in
# the +options+.
#
- # session.use 'host', 'user'
- # session.use 'host2', 'user2', :via => nil
- # session.use 'host3', 'user3', :via => Net::SSH::Gateway.new('gateway.host', 'user')
- def use(host, user, options={})
- server = Server.new(self, host, user, {:via => default_gateway}.merge(options))
+ # session.use 'host'
+ # session.use 'user@host2', :via => nil
+ # session.use 'host3', :user => "user3", :via => Net::SSH::Gateway.new('gateway.host', 'user')
+ def use(host, options={})
+ server = Server.new(self, host, {:via => default_gateway}.merge(options))
exists = servers.index(server)
if exists
server = servers[exists]
diff --git a/test/server_test.rb b/test/server_test.rb
index caca334..02ba0e0 100644
--- a/test/server_test.rb
+++ b/test/server_test.rb
@@ -3,80 +3,96 @@ require 'net/ssh/multi/server'
class ServerTest < Test::Unit::TestCase
def setup
- @master = mock('multi-session')
+ @master = stub('multi-session', :default_user => "bob")
end
def test_accessor_without_properties_should_access_empty_hash
- assert_nil server('host', 'user')[:foo]
+ assert_nil server('host')[:foo]
end
def test_accessor_with_properties_should_access_properties
- assert_equal "hello", server('host', 'user', :properties => { :foo => "hello" })[:foo]
+ assert_equal "hello", server('host', :properties => { :foo => "hello" })[:foo]
end
def test_port_should_return_22_by_default
- assert_equal 22, server('host', 'user').port
+ assert_equal 22, server('host').port
end
def test_port_should_return_given_port_when_present
- assert_equal 1234, server('host', 'user', :port => 1234).port
+ assert_equal 1234, server('host', :port => 1234).port
+ end
+
+ def test_port_should_return_parsed_port_when_present
+ assert_equal 1234, server('host:1234', :port => 1235).port
+ end
+
+ def test_user_should_return_default_user_by_default
+ assert_equal "bob", server('host').user
+ end
+
+ def test_user_should_return_given_user_when_present
+ assert_equal "jim", server('host', :user => "jim").user
+ end
+
+ def test_user_should_return_parsed_user_when_present
+ assert_equal "jim", server('jim@host', :user => "john").user
end
def test_equivalence_when_host_and_user_and_port_match
- s1 = server('host', 'user', :port => 1234)
- s2 = server('host', 'user', :port => 1234)
+ s1 = server('user@host:1234')
+ s2 = server('user@host:1234')
assert s1.eql?(s2)
assert_equal s1.hash, s2.hash
assert s1 == s2
end
def test_equivalence_when_host_mismatch
- s1 = server('host1', 'user', :port => 1234)
- s2 = server('host2', 'user', :port => 1234)
+ s1 = server('user@host1:1234')
+ s2 = server('user@host2:1234')
assert !s1.eql?(s2)
assert_not_equal s1.hash, s2.hash
assert s1 != s2
end
def test_equivalence_when_port_mismatch
- s1 = server('host', 'user', :port => 1234)
- s2 = server('host', 'user', :port => 1235)
+ s1 = server('user@host:1234')
+ s2 = server('user@host:1235')
assert !s1.eql?(s2)
assert_not_equal s1.hash, s2.hash
assert s1 != s2
end
def test_equivalence_when_user_mismatch
- s1 = server('host', 'user1', :port => 1234)
- s2 = server('host', 'user2', :port => 1234)
+ s1 = server('user1@host:1234')
+ s2 = server('user2@host:1234')
assert !s1.eql?(s2)
assert_not_equal s1.hash, s2.hash
assert s1 != s2
end
def test_to_s_should_include_user_and_host
- assert_equal "user@host", server('host', 'user').to_s
+ assert_equal "user@host", server('user@host').to_s
end
def test_to_s_should_include_user_and_host_and_port_when_port_is_given
- assert_equal "user@host:1234", server('host', 'user', :port => 1234).to_s
+ assert_equal "user@host:1234", server('user@host:1234').to_s
end
def test_gateway_should_be_nil_by_default
- assert_nil server('host', 'user').gateway
+ assert_nil server('host').gateway
end
def test_gateway_should_be_set_with_the_via_value
gateway = mock('gateway')
- assert_equal gateway, server('host', 'user', :via => gateway).gateway
+ assert_equal gateway, server('host', :via => gateway).gateway
end
def test_session_with_default_argument_should_not_instantiate_session
- assert_nil server('host', 'user').session
+ assert_nil server('host').session
end
def test_session_with_true_argument_should_instantiate_and_cache_session
- srv = server('host', 'user', :port => 1234)
+ srv = server('host')
session = expect_connection_to(srv)
assert_equal session, srv.session(true)
assert_equal session, srv.session(true)
@@ -84,23 +100,23 @@ class ServerTest < Test::Unit::TestCase
end
def test_session_that_cannot_authenticate_adds_host_to_exception_message
- srv = server('host', 'user')
- Net::SSH.expects(:start).with('host', 'user', {}).raises(Net::SSH::AuthenticationFailed.new('user'))
+ srv = server('host')
+ Net::SSH.expects(:start).with('host', 'bob', {}).raises(Net::SSH::AuthenticationFailed.new('bob'))
begin
srv.new_session
flunk
rescue Net::SSH::AuthenticationFailed => e
- assert_equal "user@host", e.message
+ assert_equal "bob@host", e.message
end
end
def test_close_channels_when_session_is_not_open_should_not_do_anything
- assert_nothing_raised { server('host', 'user').close_channels }
+ assert_nothing_raised { server('host').close_channels }
end
def test_close_channels_when_session_is_open_should_iterate_over_open_channels_and_close_them
- srv = server('host', 'user')
+ srv = server('host')
session = expect_connection_to(srv)
c1 = mock('channel', :close => nil)
c2 = mock('channel', :close => nil)
@@ -111,11 +127,11 @@ class ServerTest < Test::Unit::TestCase
end
def test_close_when_session_is_not_open_should_not_do_anything
- assert_nothing_raised { server('host', 'user').close }
+ assert_nothing_raised { server('host').close }
end
def test_close_when_session_is_open_should_close_session
- srv = server('host', 'user')
+ srv = server('host')
session = expect_connection_to(srv)
session.expects(:close)
@master.expects(:server_closed).with(srv)
@@ -124,11 +140,11 @@ class ServerTest < Test::Unit::TestCase
end
def test_busy_should_be_false_when_session_is_not_open
- assert !server('host', 'user').busy?
+ assert !server('host').busy?
end
def test_busy_should_be_false_when_session_is_not_busy
- srv = server('host', 'user')
+ srv = server('host')
session = expect_connection_to(srv)
session.expects(:busy?).returns(false)
srv.session(true)
@@ -136,7 +152,7 @@ class ServerTest < Test::Unit::TestCase
end
def test_busy_should_be_true_when_session_is_busy
- srv = server('host', 'user')
+ srv = server('host')
session = expect_connection_to(srv)
session.expects(:busy?).returns(true)
srv.session(true)
@@ -144,11 +160,11 @@ class ServerTest < Test::Unit::TestCase
end
def test_preprocess_should_be_nil_when_session_is_not_open
- assert_nil server('host', 'user').preprocess
+ assert_nil server('host').preprocess
end
def test_preprocess_should_return_result_of_session_preprocess
- srv = server('host', 'user')
+ srv = server('host')
session = expect_connection_to(srv)
session.expects(:preprocess).returns(:result)
srv.session(true)
@@ -156,11 +172,11 @@ class ServerTest < Test::Unit::TestCase
end
def test_readers_should_return_empty_array_when_session_is_not_open
- assert_equal [], server('host', 'user').readers
+ assert_equal [], server('host').readers
end
def test_readers_should_return_all_listeners_when_session_is_open
- srv = server('host', 'user')
+ srv = server('host')
session = expect_connection_to(srv)
session.expects(:listeners).returns(1 => 2, 3 => 4, 5 => 6, 7 => 8)
srv.session(true)
@@ -168,11 +184,11 @@ class ServerTest < Test::Unit::TestCase
end
def test_writers_should_return_empty_array_when_session_is_not_open
- assert_equal [], server('host', 'user').writers
+ assert_equal [], server('host').writers
end
def test_writers_should_return_all_listeners_that_are_pending_writes_when_session_is_open
- srv = server('host', 'user')
+ srv = server('host')
session = expect_connection_to(srv)
listeners = { writer(:ready) => 1, writer(:reader) => 2,
writer(:reader) => 3, writer(:idle) => 4, writer(:ready) => 5 }
@@ -182,11 +198,11 @@ class ServerTest < Test::Unit::TestCase
end
def test_postprocess_should_return_true_when_session_is_not_open
- assert_equal true, server('host', 'user').postprocess([], [])
+ assert_equal true, server('host').postprocess([], [])
end
def test_postprocess_should_call_session_postprocess_with_ios_belonging_to_session
- srv = server('host', 'user')
+ srv = server('host')
session = expect_connection_to(srv)
session.expects(:listeners).returns(1 => 2, 3 => 4, 5 => 6, 7 => 8)
session.expects(:postprocess).with([1,3], [7]).returns(:result)
@@ -196,8 +212,8 @@ class ServerTest < Test::Unit::TestCase
private
- def server(host, user, options={})
- Net::SSH::Multi::Server.new(@master, host, user, options)
+ def server(host, options={})
+ Net::SSH::Multi::Server.new(@master, host, options)
end
def expect_connection_to(server)
diff --git a/test/session_actions_test.rb b/test/session_actions_test.rb
index c4f3e58..6c688af 100644
--- a/test/session_actions_test.rb
+++ b/test/session_actions_test.rb
@@ -12,8 +12,12 @@ class SessionActionsTest < Test::Unit::TestCase
@servers = []
end
- def use(h, u, o={})
- server = Net::SSH::Multi::Server.new(self, h, u, o)
+ def default_user
+ "user"
+ end
+
+ def use(h, o={})
+ server = Net::SSH::Multi::Server.new(self, h, o)
servers << server
server
end
@@ -24,7 +28,7 @@ class SessionActionsTest < Test::Unit::TestCase
end
def test_busy_should_be_true_if_any_server_is_busy
- srv1, srv2, srv3 = @session.use('h1', 'u1'), @session.use('h2', 'u2'), @session.use('h3', 'u3')
+ srv1, srv2, srv3 = @session.use('h1'), @session.use('h2'), @session.use('h3')
srv1.stubs(:busy?).returns(false)
srv2.stubs(:busy?).returns(false)
srv3.stubs(:busy?).returns(true)
@@ -32,7 +36,7 @@ class SessionActionsTest < Test::Unit::TestCase
end
def test_busy_should_be_false_if_all_servers_are_not_busy
- srv1, srv2, srv3 = @session.use('h1', 'u1', :properties => {:a => 1}), @session.use('h2', 'u2', :properties => {:a => 1, :b => 2}), @session.use('h3', 'u3')
+ srv1, srv2, srv3 = @session.use('h1'), @session.use('h2'), @session.use('h3')
srv1.stubs(:busy?).returns(false)
srv2.stubs(:busy?).returns(false)
srv3.stubs(:busy?).returns(false)
@@ -51,8 +55,8 @@ class SessionActionsTest < Test::Unit::TestCase
end
def test_open_channel_should_delegate_to_sessions_and_set_accessors_on_each_channel_and_return_multi_channel
- srv1 = @session.use('h1', 'u1')
- srv2 = @session.use('h2', 'u2')
+ srv1 = @session.use('h1')
+ srv2 = @session.use('h2')
s1 = { :server => srv1 }
s2 = { :server => srv2 }
c1 = { :stub => :value }
diff --git a/test/session_test.rb b/test/session_test.rb
index 288275a..6446ace 100644
--- a/test/session_test.rb
+++ b/test/session_test.rb
@@ -44,7 +44,7 @@ class SessionTest < Test::Unit::TestCase
def test_use_should_add_new_server_to_server_list
@session.open_groups.concat([:first, :second])
- server = @session.use('host', 'user', :a => :b)
+ server = @session.use('user@host', :a => :b)
assert_equal [server], @session.servers
assert_equal 'host', server.host
assert_equal 'user', server.user
@@ -54,7 +54,7 @@ class SessionTest < Test::Unit::TestCase
def test_use_with_open_groups_should_add_new_server_to_server_list_and_groups
@session.open_groups.concat([:first, :second])
- server = @session.use('host', 'user')
+ server = @session.use('host')
assert_equal [server], @session.groups[:first]
assert_equal [server], @session.groups[:second]
end
@@ -62,13 +62,13 @@ class SessionTest < Test::Unit::TestCase
def test_use_with_default_gateway_should_set_gateway_on_server
Net::SSH::Gateway.expects(:new).with('host', 'user', {}).returns(:gateway)
@session.via('host', 'user')
- server = @session.use('host2', 'user2')
+ server = @session.use('host2')
assert_equal :gateway, server.gateway
end
def test_use_with_duplicate_server_will_not_add_server_twice
- s1 = @session.use('host', 'user')
- s2 = @session.use('host', 'user')
+ s1 = @session.use('host')
+ s2 = @session.use('host')
assert_equal 1, @session.servers.length
assert_equal s1.object_id, s2.object_id
end
@@ -96,14 +96,14 @@ class SessionTest < Test::Unit::TestCase
end
def test_on_should_return_subsession_containing_only_the_given_servers
- s1 = @session.use('h1', 'u1')
- s2 = @session.use('h2', 'u2')
+ s1 = @session.use('h1')
+ s2 = @session.use('h2')
subsession = @session.on(s1, s2)
assert_equal [s1, s2], subsession.servers
end
def test_on_should_yield_subsession_if_block_is_given
- s1 = @session.use('h1', 'u1')
+ s1 = @session.use('h1')
yielded = nil
result = @session.on(s1) do |s|
yielded = s
@@ -113,30 +113,30 @@ class SessionTest < Test::Unit::TestCase
end
def test_servers_for_should_return_all_servers_if_no_arguments
- srv1, srv2, srv3 = @session.use('h1', 'u1'), @session.use('h2', 'u2'), @session.use('h3', 'u3')
- assert_equal %w(h1 h2 h3), @session.servers_for.map { |s| s.host }.sort
+ srv1, srv2, srv3 = @session.use('h1'), @session.use('h2'), @session.use('h3')
+ assert_equal [srv1, srv2, srv3], @session.servers_for.sort
end
def test_servers_for_should_return_servers_only_for_given_group
- srv1, srv2, srv3 = @session.use('h1', 'u1'), @session.use('h2', 'u2'), @session.use('h3', 'u3')
+ srv1, srv2, srv3 = @session.use('h1'), @session.use('h2'), @session.use('h3')
@session.group :app => [srv1, srv2], :db => [srv3]
- assert_equal %w(h1 h2), @session.servers_for(:app).map { |s| s.host }.sort
+ assert_equal [srv1, srv2], @session.servers_for(:app).sort
end
def test_servers_for_should_not_return_duplicate_servers
- srv1, srv2, srv3 = @session.use('h1', 'u1'), @session.use('h2', 'u2'), @session.use('h3', 'u3')
+ srv1, srv2, srv3 = @session.use('h1'), @session.use('h2'), @session.use('h3')
@session.group :app => [srv1, srv2], :db => [srv2, srv3]
- assert_equal ["h1", "h2", "h3"], @session.servers_for(:app, :db).map { |s| s.host }.sort
+ assert_equal [srv1, srv2, srv3], @session.servers_for(:app, :db).sort
end
def test_servers_for_should_correctly_apply_only_and_except_constraints
- srv1, srv2, srv3 = @session.use('h1', 'u1', :properties => {:a => 1}), @session.use('h2', 'u2', :properties => {:a => 1, :b => 2}), @session.use('h3', 'u3')
+ srv1, srv2, srv3 = @session.use('h1', :properties => {:a => 1}), @session.use('h2', :properties => {:a => 1, :b => 2}), @session.use('h3')
@session.group :app => [srv1, srv2, srv3]
assert_equal [srv1], @session.servers_for(:app => {:only => {:a => 1}, :except => {:b => 2}})
end
def test_close_should_close_server_sessions
- srv1, srv2 = @session.use('h1', 'u1'), @session.use('h2', 'u2')
+ srv1, srv2 = @session.use('h1'), @session.use('h2')
srv1.expects(:close_channels)
srv2.expects(:close_channels)
srv1.expects(:close)
@@ -160,25 +160,25 @@ class SessionTest < Test::Unit::TestCase
end
def test_preprocess_should_immediately_return_false_if_block_returns_false
- srv = @session.use('h1', 'u1')
+ srv = @session.use('h1')
srv.expects(:preprocess).never
assert_equal false, @session.preprocess { false }
end
def test_preprocess_should_call_preprocess_on_component_servers
- srv = @session.use('h1', 'u1')
+ srv = @session.use('h1')
srv.expects(:preprocess)
assert_equal :hello, @session.preprocess { :hello }
end
def test_preprocess_should_succeed_even_without_block
- srv = @session.use('h1', 'u1')
+ srv = @session.use('h1')
srv.expects(:preprocess)
assert_equal true, @session.preprocess
end
def test_postprocess_should_call_postprocess_on_component_servers
- srv = @session.use('h1', 'u1')
+ srv = @session.use('h1')
srv.expects(:postprocess).with([:a], [:b])
assert_equal true, @session.postprocess([:a], [:b])
end
@@ -189,7 +189,7 @@ class SessionTest < Test::Unit::TestCase
def test_process_should_call_select_on_combined_readers_and_writers_from_all_servers
@session.expects(:postprocess).with([:b, :c], [:a, :c])
- srv1, srv2, srv3 = @session.use('h1', 'u1'), @session.use('h2', 'u2'), @session.use('h3', 'u3')
+ srv1, srv2, srv3 = @session.use('h1'), @session.use('h2'), @session.use('h3')
srv1.expects(:readers).returns([:a])
srv1.expects(:writers).returns([:a])
srv2.expects(:readers).returns([])