aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--switchtower/CHANGELOG3
-rw-r--r--switchtower/lib/switchtower/actor.rb38
-rw-r--r--switchtower/lib/switchtower/command.rb2
-rw-r--r--switchtower/test/actor_test.rb47
-rw-r--r--switchtower/test/command_test.rb43
5 files changed, 96 insertions, 37 deletions
diff --git a/switchtower/CHANGELOG b/switchtower/CHANGELOG
new file mode 100644
index 0000000000..e8681cfaeb
--- /dev/null
+++ b/switchtower/CHANGELOG
@@ -0,0 +1,3 @@
+*SVN*
+
+* When executing multiline commands, use a backslash to escape the newline
diff --git a/switchtower/lib/switchtower/actor.rb b/switchtower/lib/switchtower/actor.rb
index cbaf8463e1..db25ca99c2 100644
--- a/switchtower/lib/switchtower/actor.rb
+++ b/switchtower/lib/switchtower/actor.rb
@@ -12,6 +12,27 @@ module SwitchTower
# new actor via Configuration#actor.
class Actor
+ # An adaptor for making the Net::SSH interface look and act like that of the
+ # Gateway class.
+ class DefaultConnectionFactory #:nodoc:
+ def initialize(config)
+ @config= config
+ end
+
+ def connect_to(server)
+ Net::SSH.start(server, :username => @config.user,
+ :password => @config.password)
+ end
+ end
+
+ class <<self
+ attr_accessor :connection_factory
+ attr_accessor :command_factory
+ end
+
+ self.connection_factory = DefaultConnectionFactory
+ self.command_factory = Command
+
# The configuration instance associated with this actor.
attr_reader :configuration
@@ -37,19 +58,6 @@ module SwitchTower
# A struct for representing a single instance of an invoked task.
TaskCallFrame = Struct.new(:name, :rollback)
- # An adaptor for making the Net::SSH interface look and act like that of the
- # Gateway class.
- class DefaultConnectionFactory #:nodoc:
- def initialize(config)
- @config= config
- end
-
- def connect_to(server)
- Net::SSH.start(server, :username => @config.user,
- :password => @config.password)
- end
- end
-
# Represents the definition of a single task.
class Task #:nodoc:
attr_reader :name, :options
@@ -88,7 +96,7 @@ module SwitchTower
@tasks = {}
@task_call_frames = []
@sessions = {}
- @factory = DefaultConnectionFactory.new(configuration)
+ @factory = self.class.connection_factory.new(configuration)
end
# Define a new task for this actor. The block will be invoked when this
@@ -134,7 +142,7 @@ module SwitchTower
establish_connections(servers)
# execute the command on each server in parallel
- command = Command.new(servers, cmd, block, options, self)
+ command = self.class.command_factory.new(servers, cmd, block, options, self)
command.process! # raises an exception if command fails on any server
end
end
diff --git a/switchtower/lib/switchtower/command.rb b/switchtower/lib/switchtower/command.rb
index 910c97cb45..807958fd76 100644
--- a/switchtower/lib/switchtower/command.rb
+++ b/switchtower/lib/switchtower/command.rb
@@ -7,7 +7,7 @@ module SwitchTower
def initialize(servers, command, callback, options, actor) #:nodoc:
@servers = servers
- @command = command
+ @command = command.gsub(/\r?\n/, "\\\n")
@callback = callback
@options = options
@actor = actor
diff --git a/switchtower/test/actor_test.rb b/switchtower/test/actor_test.rb
index bc33750b83..ed657ee26e 100644
--- a/switchtower/test/actor_test.rb
+++ b/switchtower/test/actor_test.rb
@@ -5,28 +5,24 @@ require 'test/unit'
require 'switchtower/actor'
require 'switchtower/logger'
-module SwitchTower
- class Actor
- attr_reader :factory
+class ActorTest < Test::Unit::TestCase
- class DefaultConnectionFactory
- def connect_to(server)
- server
- end
+ class TestingConnectionFactory
+ def initialize(config)
end
- class GatewayConnectionFactory
- def connect_to(server)
- server
- end
+ def connect_to(server)
+ server
end
+ end
- def establish_gateway
- GatewayConnectionFactory.new
+ class GatewayConnectionFactory
+ def connect_to(server)
+ server
end
end
- class Command
+ class TestingCommand
def self.invoked!
@invoked = true
end
@@ -46,9 +42,18 @@ module SwitchTower
self.class.invoked!
end
end
-end
-class ActorTest < Test::Unit::TestCase
+ class TestActor < SwitchTower::Actor
+ attr_reader :factory
+
+ self.connection_factory = TestingConnectionFactory
+ self.command_factory = TestingCommand
+
+ def establish_gateway
+ GatewayConnectionFactory.new
+ end
+ end
+
class MockConfiguration
Role = Struct.new(:host, :options)
@@ -79,8 +84,8 @@ class ActorTest < Test::Unit::TestCase
end
def setup
- SwitchTower::Command.reset!
- @actor = SwitchTower::Actor.new(MockConfiguration.new)
+ TestingCommand.reset!
+ @actor = TestActor.new(MockConfiguration.new)
end
def test_define_task_creates_method
@@ -203,7 +208,7 @@ class ActorTest < Test::Unit::TestCase
end
@actor.foo
- assert_instance_of SwitchTower::Actor::GatewayConnectionFactory, @actor.factory
+ assert_instance_of GatewayConnectionFactory, @actor.factory
end
def test_run_when_not_pretend
@@ -213,7 +218,7 @@ class ActorTest < Test::Unit::TestCase
@actor.configuration.pretend = false
@actor.foo
- assert SwitchTower::Command.invoked?
+ assert TestingCommand.invoked?
end
def test_run_when_pretend
@@ -223,7 +228,7 @@ class ActorTest < Test::Unit::TestCase
@actor.configuration.pretend = true
@actor.foo
- assert !SwitchTower::Command.invoked?
+ assert !TestingCommand.invoked?
end
def test_task_before_hook
diff --git a/switchtower/test/command_test.rb b/switchtower/test/command_test.rb
new file mode 100644
index 0000000000..7005ba0f2d
--- /dev/null
+++ b/switchtower/test/command_test.rb
@@ -0,0 +1,43 @@
+$:.unshift File.dirname(__FILE__) + "/../lib"
+
+require 'stringio'
+require 'test/unit'
+require 'switchtower/command'
+
+class CommandTest < Test::Unit::TestCase
+ class MockSession
+ def open_channel
+ { :closed => true, :status => 0 }
+ end
+ end
+
+ class MockActor
+ attr_reader :sessions
+
+ def initialize
+ @sessions = Hash.new { |h,k| h[k] = MockSession.new }
+ end
+ end
+
+ def setup
+ @actor = MockActor.new
+ end
+
+ def test_command_executes_on_all_servers
+ command = SwitchTower::Command.new(%w(server1 server2 server3),
+ "hello", nil, {}, @actor)
+ assert_equal %w(server1 server2 server3), @actor.sessions.keys.sort
+ end
+
+ def test_command_with_newlines
+ command = SwitchTower::Command.new(%w(server1), "hello\nworld", nil, {},
+ @actor)
+ assert_equal "hello\\\nworld", command.command
+ end
+
+ def test_command_with_windows_newlines
+ command = SwitchTower::Command.new(%w(server1), "hello\r\nworld", nil, {},
+ @actor)
+ assert_equal "hello\\\nworld", command.command
+ end
+end