Skip to content

Commit

Permalink
When executing multiline commands, escape newlines with a backslash
Browse files Browse the repository at this point in the history
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1975 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information
jamis committed Aug 6, 2005
1 parent 25ce9f3 commit 5e558bc
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 37 deletions.
3 changes: 3 additions & 0 deletions switchtower/CHANGELOG
@@ -0,0 +1,3 @@
*SVN*

* When executing multiline commands, use a backslash to escape the newline
38 changes: 23 additions & 15 deletions switchtower/lib/switchtower/actor.rb
Expand Up @@ -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

Expand All @@ -37,19 +58,6 @@ class Actor
# 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
Expand Down Expand Up @@ -88,7 +96,7 @@ def initialize(config) #:nodoc:
@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
Expand Down Expand Up @@ -134,7 +142,7 @@ def run(cmd, options={}, &block)
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
Expand Down
2 changes: 1 addition & 1 deletion switchtower/lib/switchtower/command.rb
Expand Up @@ -7,7 +7,7 @@ class Command

def initialize(servers, command, callback, options, actor) #:nodoc:
@servers = servers
@command = command
@command = command.gsub(/\r?\n/, "\\\n")
@callback = callback
@options = options
@actor = actor
Expand Down
47 changes: 26 additions & 21 deletions switchtower/test/actor_test.rb
Expand Up @@ -5,28 +5,24 @@
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
Expand All @@ -46,9 +42,18 @@ def process!
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)

Expand Down Expand Up @@ -79,8 +84,8 @@ def logger
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
Expand Down Expand Up @@ -203,7 +208,7 @@ def test_establish_connection_uses_gateway_if_specified
end

@actor.foo
assert_instance_of SwitchTower::Actor::GatewayConnectionFactory, @actor.factory
assert_instance_of GatewayConnectionFactory, @actor.factory
end

def test_run_when_not_pretend
Expand All @@ -213,7 +218,7 @@ def test_run_when_not_pretend

@actor.configuration.pretend = false
@actor.foo
assert SwitchTower::Command.invoked?
assert TestingCommand.invoked?
end

def test_run_when_pretend
Expand All @@ -223,7 +228,7 @@ def test_run_when_pretend

@actor.configuration.pretend = true
@actor.foo
assert !SwitchTower::Command.invoked?
assert !TestingCommand.invoked?
end

def test_task_before_hook
Expand Down
43 changes: 43 additions & 0 deletions 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

0 comments on commit 5e558bc

Please sign in to comment.