mirror of
https://github.com/capistrano/capistrano
synced 2023-03-27 23:21:18 -04:00
start refactoring the task helper actions
git-svn-id: http://svn.rubyonrails.org/rails/tools/capistrano@6288 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
acdd65f213
commit
472431004c
13 changed files with 345 additions and 168 deletions
1
Rakefile
1
Rakefile
|
@ -19,7 +19,6 @@ desc "Build documentation"
|
|||
task :doc => [ :rdoc ]
|
||||
|
||||
Rake::TestTask.new do |t|
|
||||
t.ruby_opts << "-rubygems"
|
||||
t.test_files = Dir["test/**/*_test.rb"]
|
||||
t.verbose = true
|
||||
end
|
||||
|
|
|
@ -13,42 +13,6 @@ module Capistrano
|
|||
# directly--rather, you create a new Configuration instance, and access the
|
||||
# new actor via Configuration#actor.
|
||||
class Actor
|
||||
class <<self
|
||||
attr_accessor :default_io_proc
|
||||
end
|
||||
|
||||
self.default_io_proc = Proc.new do |ch, stream, out|
|
||||
level = stream == :err ? :important : :info
|
||||
ch[:actor].logger.send(level, out, "#{stream} :: #{ch[:host]}")
|
||||
end
|
||||
|
||||
def initialize(config) #:nodoc:
|
||||
@configuration = config
|
||||
@tasks = {}
|
||||
@task_call_frames = []
|
||||
@sessions = {}
|
||||
@factory = self.class.connection_factory.new(configuration)
|
||||
end
|
||||
|
||||
# Execute the given command on all servers that are the target of the
|
||||
# current task. If a block is given, it is invoked for all output
|
||||
# generated by the command, and should accept three parameters: the SSH
|
||||
# channel (which may be used to send data back to the remote process),
|
||||
# the stream identifier (<tt>:err</tt> for stderr, and <tt>:out</tt> for
|
||||
# stdout), and the data that was received.
|
||||
#
|
||||
# If +pretend+ mode is active, this does nothing.
|
||||
def run(cmd, options={}, &block)
|
||||
block ||= default_io_proc
|
||||
logger.debug "executing #{cmd.strip.inspect}"
|
||||
|
||||
execute_on_servers(options) do |servers|
|
||||
# execute the command on each server in parallel
|
||||
command = self.class.command_factory.new(servers, cmd, block, options, self)
|
||||
command.process! # raises an exception if command fails on any server
|
||||
end
|
||||
end
|
||||
|
||||
# Streams the result of the command from all servers that are the target of the
|
||||
# current task. All these streams will be joined into a single one,
|
||||
# so you can, say, watch 10 log files as though they were one. Do note that this
|
||||
|
@ -78,56 +42,6 @@ module Capistrano
|
|||
run(cmd, options)
|
||||
end
|
||||
|
||||
# Store the given data at the given location on all servers targetted 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={})
|
||||
if Capistrano::SFTP
|
||||
execute_on_servers(options) do |servers|
|
||||
transfer = self.class.transfer_factory.new(servers, self, path, :data => data,
|
||||
:mode => options[:mode])
|
||||
transfer.process!
|
||||
end
|
||||
else
|
||||
# Poor-man's SFTP... just run a cat on the remote end, and send data
|
||||
# to it.
|
||||
|
||||
cmd = "cat > #{path}"
|
||||
cmd << " && chmod #{options[:mode].to_s(8)} #{path}" if options[:mode]
|
||||
run(cmd, options.merge(:data => data + "\n\4")) do |ch, stream, out|
|
||||
logger.important out, "#{stream} :: #{ch[:host]}" if stream == :err
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Get file remote_path from FIRST server targetted by
|
||||
# the current task and transfer it to local machine as path. It will use
|
||||
# SFTP if Net::SFTP is installed; otherwise it will fall back to using
|
||||
# 'cat', which may cause corruption in binary files.
|
||||
#
|
||||
# get "#{deploy_to}/current/log/production.log", "log/production.log.web"
|
||||
def get(remote_path, path, options = {})
|
||||
if Capistrano::SFTP && options.fetch(:sftp, true)
|
||||
execute_on_servers(options.merge(:once => true)) do |servers|
|
||||
logger.debug "downloading #{servers.first}:#{remote_path} to #{path}"
|
||||
sftp = sessions[servers.first].sftp
|
||||
sftp.connect unless sftp.state == :open
|
||||
sftp.get_file remote_path, path
|
||||
logger.trace "download finished"
|
||||
end
|
||||
else
|
||||
logger.important "Net::SFTP is not available; using remote 'cat' to get file, which may cause file corruption"
|
||||
File.open(path, "w") do |destination|
|
||||
run "cat #{remote_path}", :once => true do |ch, stream, data|
|
||||
case stream
|
||||
when :out then destination << data
|
||||
when :err then raise "error while downloading #{remote_path}: #{data.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Executes the given command on the first server targetted by the current
|
||||
# task, collects it's stdout into a string, and returns the string.
|
||||
def capture(command, options={})
|
||||
|
@ -141,42 +55,6 @@ module Capistrano
|
|||
output
|
||||
end
|
||||
|
||||
# Like #run, but executes the command via <tt>sudo</tt>. This assumes that
|
||||
# the sudo password (if required) is the same as the password for logging
|
||||
# in to the server.
|
||||
#
|
||||
# Also, this module accepts a <tt>:sudo</tt> configuration variable,
|
||||
# which (if specified) will be used as the full path to the sudo
|
||||
# executable on the remote machine:
|
||||
#
|
||||
# set :sudo, "/opt/local/bin/sudo"
|
||||
def sudo(command, options={}, &block)
|
||||
block ||= default_io_proc
|
||||
|
||||
# in order to prevent _each host_ from prompting when the password was
|
||||
# wrong, let's track which host prompted first and only allow subsequent
|
||||
# prompts from that host.
|
||||
prompt_host = nil
|
||||
user = options[:as].nil? ? '' : "-u #{options[:as]}"
|
||||
|
||||
run "#{sudo_command} #{user} #{command}", options do |ch, stream, out|
|
||||
if out =~ /^Password:/
|
||||
ch.send_data "#{password}\n"
|
||||
elsif out =~ /try again/
|
||||
if prompt_host.nil? || prompt_host == ch[:host]
|
||||
prompt_host = ch[:host]
|
||||
logger.important out, "#{stream} :: #{ch[:host]}"
|
||||
# reset the password to it's original value and prepare for another
|
||||
# pass (the reset allows the password prompt to be attempted again
|
||||
# if the password variable was originally a proc (the default)
|
||||
set :password, self[:original_value][:password] || self[:password]
|
||||
end
|
||||
else
|
||||
block.call(ch, stream, out)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Renders an ERb template and returns the result. This is useful for
|
||||
# dynamically building documents to store on the remote servers.
|
||||
#
|
||||
|
@ -230,41 +108,5 @@ module Capistrano
|
|||
raise ArgumentError, "no file or template given for rendering"
|
||||
end
|
||||
end
|
||||
|
||||
# An instance-level reader for the class' #default_io_proc attribute.
|
||||
def default_io_proc
|
||||
self.class.default_io_proc
|
||||
end
|
||||
|
||||
# Used to force connections to be made to the current task's servers.
|
||||
# Connections are normally made lazily in Capistrano--you can use this
|
||||
# to force them open before performing some operation that might be
|
||||
# time-sensitive.
|
||||
def connect!(options={})
|
||||
execute_on_servers(options) { }
|
||||
end
|
||||
|
||||
def metaclass
|
||||
class << self; self; end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sudo_command
|
||||
configuration[:sudo] || "sudo"
|
||||
end
|
||||
|
||||
def define_method(name, &block)
|
||||
metaclass.send(:define_method, name, &block)
|
||||
end
|
||||
|
||||
def method_missing(sym, *args, &block)
|
||||
if @configuration.respond_to?(sym)
|
||||
@configuration.send(sym, *args, &block)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,20 @@ module Capistrano
|
|||
|
||||
attr_reader :command, :sessions, :options
|
||||
|
||||
def initialize(command, sessions, options={}, &block) #:nodoc:
|
||||
def self.process(command, sessions, options={}, &block)
|
||||
new(command, sessions, options, &block).process!
|
||||
end
|
||||
|
||||
# Instantiates a new command object. The +command+ must be a string
|
||||
# containing the command to execute. +sessions+ is an array of Net::SSH
|
||||
# session instances, and +options+ must be a hash containing any of the
|
||||
# following keys:
|
||||
#
|
||||
# * +logger+: (optional), a Capistrano::Logger instance
|
||||
# * +data+: (optional), a string to be sent to the command via it's stdin
|
||||
# * +env+: (optional), a string or hash to be interpreted as environment
|
||||
# variables that should be defined for this command invocation.
|
||||
def initialize(command, sessions, options={}, &block)
|
||||
@command = extract_environment(options) + command.strip.gsub(/\r?\n/, "\\\n")
|
||||
@sessions = sessions
|
||||
@options = options
|
||||
|
@ -15,10 +28,6 @@ module Capistrano
|
|||
@channels = open_channels
|
||||
end
|
||||
|
||||
def logger #:nodoc:
|
||||
options[:logger]
|
||||
end
|
||||
|
||||
# Processes the command in parallel on all specified hosts. If the command
|
||||
# fails (non-zero return code) on any of the hosts, this will raise a
|
||||
# RuntimeError.
|
||||
|
@ -59,6 +68,10 @@ module Capistrano
|
|||
|
||||
private
|
||||
|
||||
def logger
|
||||
options[:logger]
|
||||
end
|
||||
|
||||
def open_channels
|
||||
sessions.map do |session|
|
||||
session.open_channel do |channel|
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
require 'capistrano/logger'
|
||||
require 'capistrano/extensions'
|
||||
|
||||
require 'capistrano/configuration/connections'
|
||||
require 'capistrano/configuration/execution'
|
||||
require 'capistrano/configuration/loading'
|
||||
|
@ -7,6 +8,8 @@ require 'capistrano/configuration/namespaces'
|
|||
require 'capistrano/configuration/roles'
|
||||
require 'capistrano/configuration/variables'
|
||||
|
||||
require 'capistrano/configuration/actions/invocation'
|
||||
|
||||
module Capistrano
|
||||
# Represents a specific Capistrano configuration. A Configuration instance
|
||||
# may be used to load multiple recipe files, define and describe tasks,
|
||||
|
@ -22,5 +25,8 @@ module Capistrano
|
|||
# The includes must come at the bottom, since they may redefine methods
|
||||
# defined in the base class.
|
||||
include Connections, Execution, Loading, Namespaces, Roles, Variables
|
||||
|
||||
# Mix in the actions
|
||||
include Actions::FileTransfer, Actions::Invocation
|
||||
end
|
||||
end
|
||||
|
|
37
lib/capistrano/configuration/actions/file_transfer.rb
Normal file
37
lib/capistrano/configuration/actions/file_transfer.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
require 'capistrano/upload'
|
||||
|
||||
module Capistrano
|
||||
class Configuration
|
||||
module Actions
|
||||
module FileTransfer
|
||||
|
||||
# Store the given data at the given location on all servers targetted
|
||||
# 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={})
|
||||
execute_on_servers(options) do |servers|
|
||||
targets = servers.map { |s| sessions[s.host] }
|
||||
Upload.process(targets, path, :data => data, :mode => options[:mode], :logger => logger)
|
||||
end
|
||||
end
|
||||
|
||||
# Get file remote_path from FIRST server targetted by
|
||||
# the current task and transfer it to local machine as path. It will use
|
||||
# SFTP if Net::SFTP is installed; otherwise it will fall back to using
|
||||
# 'cat', which may cause corruption in binary files.
|
||||
#
|
||||
# get "#{deploy_to}/current/log/production.log", "log/production.log.web"
|
||||
def get(remote_path, path, options = {})
|
||||
execute_on_servers(options.merge(:once => true)) do |servers|
|
||||
logger.info "downloading `#{servers.first.host}:#{remote_path}' to `#{path}'"
|
||||
sftp = sessions[servers.first.host].sftp
|
||||
sftp.connect unless sftp.state == :open
|
||||
sftp.get_file remote_path, path
|
||||
logger.debug "download finished"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
86
lib/capistrano/configuration/actions/invocation.rb
Normal file
86
lib/capistrano/configuration/actions/invocation.rb
Normal file
|
@ -0,0 +1,86 @@
|
|||
require 'capistrano/command'
|
||||
|
||||
module Capistrano
|
||||
class Configuration
|
||||
module Actions
|
||||
module Invocation
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
|
||||
base.default_io_proc = Proc.new do |ch, stream, out|
|
||||
level = stream == :err ? :important : :info
|
||||
ch[:options][:logger].send(level, out, "#{stream} :: #{ch[:host]}")
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
attr_accessor :default_io_proc
|
||||
end
|
||||
|
||||
# Execute the given command on all servers that are the target of the
|
||||
# current task. If a block is given, it is invoked for all output
|
||||
# generated by the command, and should accept three parameters: the SSH
|
||||
# channel (which may be used to send data back to the remote process),
|
||||
# the stream identifier (<tt>:err</tt> for stderr, and <tt>:out</tt> for
|
||||
# stdout), and the data that was received.
|
||||
#
|
||||
# If +pretend+ mode is active, this does nothing.
|
||||
def run(cmd, options={}, &block)
|
||||
block ||= self.class.default_io_proc
|
||||
logger.debug "executing #{cmd.strip.inspect}"
|
||||
|
||||
execute_on_servers(options) do |servers|
|
||||
targets = servers.map { |s| sessions[s.host] }
|
||||
Command.process(cmd, targets, options.merge(:logger => logger), &block)
|
||||
end
|
||||
end
|
||||
|
||||
# Like #run, but executes the command via <tt>sudo</tt>. This assumes
|
||||
# that the sudo password (if required) is the same as the password for
|
||||
# logging in to the server.
|
||||
#
|
||||
# Also, this module accepts a <tt>:sudo</tt> configuration variable,
|
||||
# which (if specified) will be used as the full path to the sudo
|
||||
# executable on the remote machine:
|
||||
#
|
||||
# set :sudo, "/opt/local/bin/sudo"
|
||||
def sudo(command, options={}, &block)
|
||||
block ||= self.class.default_io_proc
|
||||
|
||||
options = options.dup
|
||||
as = options.delete(:as)
|
||||
|
||||
user = as && "-u #{as}"
|
||||
command = [fetch(:sudo, "sudo"), user, command].compact.join(" ")
|
||||
|
||||
run(command, options, &sudo_behavior_callback(block))
|
||||
end
|
||||
|
||||
# Returns a Proc object that defines the behavior of the sudo
|
||||
# callback. The returned Proc will defer to the +fallback+ argument
|
||||
# (which should also be a Proc) for any output it does not
|
||||
# explicitly handle.
|
||||
def sudo_behavior_callback(fallback) #:nodoc:
|
||||
# in order to prevent _each host_ from prompting when the password
|
||||
# was wrong, let's track which host prompted first and only allow
|
||||
# subsequent prompts from that host.
|
||||
prompt_host = nil
|
||||
|
||||
Proc.new do |ch, stream, out|
|
||||
if out =~ /^Password:/
|
||||
ch.send_data "#{self[:password]}\n"
|
||||
elsif out =~ /try again/
|
||||
if prompt_host.nil? || prompt_host == ch[:host]
|
||||
prompt_host = ch[:host]
|
||||
logger.important out, "#{stream} :: #{ch[:host]}"
|
||||
reset! :password
|
||||
end
|
||||
else
|
||||
fallback.call(ch, stream, out)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -30,6 +30,10 @@ module Capistrano
|
|||
# the requested data.
|
||||
class Error < RuntimeError; end
|
||||
|
||||
def self.process(sessions, filename, options)
|
||||
new(sessions, filename, options).process!
|
||||
end
|
||||
|
||||
attr_reader :sessions, :filename, :options
|
||||
attr_reader :failed, :completed
|
||||
|
||||
|
|
|
@ -11,14 +11,14 @@ module Capistrano
|
|||
# If +require_config+ is not false, an exception will be raised if the current
|
||||
# configuration is not set.
|
||||
def self.configuration(require_config=false)
|
||||
warn "[DEPRECATION] please use Capistrano::Configuration.instance instead of Capistrano.configuration. (You may be using a Capistrano plugin that is using this deprecated syntax.)"
|
||||
warn "[DEPRECATION] use Capistrano::Configuration.instance instead of Capistrano.configuration. (You may be using a Capistrano plugin that is using this deprecated syntax.)"
|
||||
Capistrano::Configuration.instance(require_config)
|
||||
end
|
||||
|
||||
# Used internally by Capistrano to specify the current configuration before
|
||||
# loading a third-party task bundle.
|
||||
def self.configuration=(config)
|
||||
warn "[DEPRECATION] please us Capistrano::Configuration.instance= instead of Capistrano.configuration=."
|
||||
warn "[DEPRECATION] use Capistrano::Configuration.instance= instead of Capistrano.configuration=."
|
||||
Capistrano::Configuration.instance = config
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,8 +19,8 @@ module Capistrano
|
|||
end
|
||||
|
||||
MAJOR = 1
|
||||
MINOR = 4
|
||||
TINY = 1
|
||||
MINOR = 99
|
||||
TINY = 0
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join(".")
|
||||
|
||||
|
|
38
test/configuration/actions/file_transfer_test.rb
Normal file
38
test/configuration/actions/file_transfer_test.rb
Normal file
|
@ -0,0 +1,38 @@
|
|||
require "#{File.dirname(__FILE__)}/../../utils"
|
||||
require 'capistrano/configuration/actions/file_transfer'
|
||||
|
||||
class ConfigurationActionsFileTransferTest < Test::Unit::TestCase
|
||||
class MockConfig
|
||||
include Capistrano::Configuration::Actions::FileTransfer
|
||||
end
|
||||
|
||||
def setup
|
||||
@config = MockConfig.new
|
||||
@config.stubs(:logger).returns(stub_everything)
|
||||
end
|
||||
|
||||
def test_put_should_pass_options_to_execute_on_servers
|
||||
@config.expects(:execute_on_servers).with(:foo => "bar")
|
||||
@config.put("some data", "test.txt", :foo => "bar")
|
||||
end
|
||||
|
||||
def test_put_should_delegate_to_Upload_process
|
||||
@config.expects(:execute_on_servers).yields(%w(s1 s2 s3).map { |s| mock(:host => s) })
|
||||
@config.expects(:sessions).times(3).returns(Hash.new{|h,k| h[k] = k.to_sym})
|
||||
Capistrano::Upload.expects(:process).with([:s1,:s2,:s3], "test.txt", :data => "some data", :mode => 0777, :logger => @config.logger)
|
||||
@config.put("some data", "test.txt", :mode => 0777)
|
||||
end
|
||||
|
||||
def test_get_should_pass_options_execute_on_servers_including_once
|
||||
@config.expects(:execute_on_servers).with(:foo => "bar", :once => true)
|
||||
@config.get("test.txt", "test.txt", :foo => "bar")
|
||||
end
|
||||
|
||||
def test_get_should_use_sftp_get_file_to_local_path
|
||||
sftp = mock("sftp", :state => :closed, :connect => true)
|
||||
sftp.expects(:get_file).with("remote.txt", "local.txt")
|
||||
@config.expects(:execute_on_servers).yields([stub("server", :host => "capistrano")])
|
||||
@config.expects(:sessions).returns("capistrano" => mock("session", :sftp => sftp))
|
||||
@config.get("remote.txt", "local.txt")
|
||||
end
|
||||
end
|
144
test/configuration/actions/invocation_test.rb
Normal file
144
test/configuration/actions/invocation_test.rb
Normal file
|
@ -0,0 +1,144 @@
|
|||
require "#{File.dirname(__FILE__)}/../../utils"
|
||||
require 'capistrano/configuration/actions/invocation'
|
||||
|
||||
class ConfigurationActionsRunTest < Test::Unit::TestCase
|
||||
class MockConfig
|
||||
attr_reader :options
|
||||
|
||||
def initialize
|
||||
@options = {}
|
||||
end
|
||||
|
||||
def [](*args)
|
||||
@options[*args]
|
||||
end
|
||||
|
||||
def fetch(*args)
|
||||
@options.fetch(*args)
|
||||
end
|
||||
|
||||
include Capistrano::Configuration::Actions::Invocation
|
||||
end
|
||||
|
||||
def setup
|
||||
@config = MockConfig.new
|
||||
@original_io_proc = MockConfig.default_io_proc
|
||||
@config.stubs(:logger).returns(stub_everything)
|
||||
end
|
||||
|
||||
def teardown
|
||||
MockConfig.default_io_proc = @original_io_proc
|
||||
end
|
||||
|
||||
def test_run_options_should_be_passed_to_execute_on_servers
|
||||
@config.expects(:execute_on_servers).with(:foo => "bar")
|
||||
@config.run "ls", :foo => "bar"
|
||||
end
|
||||
|
||||
def test_run_without_block_should_use_default_io_proc
|
||||
@config.expects(:execute_on_servers).yields(%w(s1 s2 s3).map { |s| mock(:host => s) })
|
||||
@config.expects(:sessions).returns(Hash.new { |h,k| h[k] = k.to_sym }).times(3)
|
||||
prepare_command("ls", [:s1, :s2, :s3], {:logger => @config.logger})
|
||||
MockConfig.default_io_proc = inspectable_proc
|
||||
@config.run "ls"
|
||||
end
|
||||
|
||||
def test_run_with_block_should_use_block
|
||||
@config.expects(:execute_on_servers).yields(%w(s1 s2 s3).map { |s| mock(:host => s) })
|
||||
@config.expects(:sessions).returns(Hash.new { |h,k| h[k] = k.to_sym }).times(3)
|
||||
prepare_command("ls", [:s1, :s2, :s3], {:logger => @config.logger})
|
||||
MockConfig.default_io_proc = Proc.new { |a,b,c| raise "shouldn't get here" }
|
||||
@config.run("ls", &inspectable_proc)
|
||||
end
|
||||
|
||||
def test_default_io_proc_should_log_stdout_arguments_as_info
|
||||
ch = { :host => "capistrano",
|
||||
:options => { :logger => mock("logger") } }
|
||||
ch[:options][:logger].expects(:info).with("data stuff", "out :: capistrano")
|
||||
MockConfig.default_io_proc[ch, :out, "data stuff"]
|
||||
end
|
||||
|
||||
def test_default_io_proc_should_log_stderr_arguments_as_important
|
||||
ch = { :host => "capistrano",
|
||||
:options => { :logger => mock("logger") } }
|
||||
ch[:options][:logger].expects(:important).with("data stuff", "err :: capistrano")
|
||||
MockConfig.default_io_proc[ch, :err, "data stuff"]
|
||||
end
|
||||
|
||||
def test_sudo_should_default_to_sudo
|
||||
@config.expects(:run).with("sudo ls", {})
|
||||
@config.sudo "ls"
|
||||
end
|
||||
|
||||
def test_sudo_should_use_sudo_variable_definition
|
||||
@config.expects(:run).with("/opt/local/bin/sudo ls", {})
|
||||
@config.options[:sudo] = "/opt/local/bin/sudo"
|
||||
@config.sudo "ls"
|
||||
end
|
||||
|
||||
def test_sudo_should_interpret_as_option_as_user
|
||||
@config.expects(:run).with("sudo -u app ls", {})
|
||||
@config.sudo "ls", :as => "app"
|
||||
end
|
||||
|
||||
def test_sudo_should_pass_options_through_to_run
|
||||
@config.expects(:run).with("sudo ls", :foo => "bar")
|
||||
@config.sudo "ls", :foo => "bar"
|
||||
end
|
||||
|
||||
def test_sudo_behavior_callback_should_send_password_when_prompted
|
||||
ch = mock("channel")
|
||||
ch.expects(:send_data).with("g00b3r\n")
|
||||
@config.options[:password] = "g00b3r"
|
||||
@config.sudo_behavior_callback(nil)[ch, nil, "Password: "]
|
||||
end
|
||||
|
||||
def test_sudo_behavior_callback_with_incorrect_password_on_first_prompt
|
||||
ch = mock("channel")
|
||||
ch.stubs(:[]).with(:host).returns("capistrano")
|
||||
@config.expects(:reset!).with(:password)
|
||||
@config.sudo_behavior_callback(nil)[ch, nil, "blah blah try again blah blah"]
|
||||
end
|
||||
|
||||
def test_sudo_behavior_callback_with_incorrect_password_on_subsequent_prompts
|
||||
callback = @config.sudo_behavior_callback(nil)
|
||||
|
||||
ch = mock("channel")
|
||||
ch.stubs(:[]).with(:host).returns("capistrano")
|
||||
ch2 = mock("channel")
|
||||
ch2.stubs(:[]).with(:host).returns("cap2")
|
||||
|
||||
@config.expects(:reset!).with(:password).times(2)
|
||||
|
||||
callback[ch, nil, "blah blah try again blah blah"]
|
||||
callback[ch2, nil, "blah blah try again blah blah"] # shouldn't call reset!
|
||||
callback[ch, nil, "blah blah try again blah blah"]
|
||||
end
|
||||
|
||||
def test_sudo_behavior_callback_should_defer_to_fallback_for_other_output
|
||||
callback = @config.sudo_behavior_callback(inspectable_proc)
|
||||
|
||||
a = mock("channel", :called => true)
|
||||
b = mock("stream", :called => true)
|
||||
c = mock("data", :called => true)
|
||||
|
||||
callback[a, b, c]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def inspectable_proc
|
||||
Proc.new do |ch, stream, data|
|
||||
ch.called
|
||||
stream.called
|
||||
data.called
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_command(command, sessions, options)
|
||||
a = mock("channel", :called => true)
|
||||
b = mock("stream", :called => true)
|
||||
c = mock("data", :called => true)
|
||||
Capistrano::Command.expects(:process).with(command, sessions, options).yields(a, b, c)
|
||||
end
|
||||
end
|
|
@ -1,4 +1,7 @@
|
|||
require "#{File.dirname(__FILE__)}/utils"
|
||||
# if the following is uncommented, the capistrano gem gets loaded if it is
|
||||
# installed, for some reason...not sure why :(
|
||||
# require 'capistrano/configuration'
|
||||
|
||||
class ConfigurationTest < Test::Unit::TestCase
|
||||
def setup
|
||||
|
|
|
@ -25,6 +25,11 @@ class UploadTest < Test::Unit::TestCase
|
|||
Capistrano::Upload.new(sessions, "test.txt", :data => "data")
|
||||
end
|
||||
|
||||
def test_self_process_should_instantiate_uploader_and_start_process
|
||||
Capistrano::Upload.expects(:new).with([:s1, :s2], "test.txt", :data => "data").returns(mock(:process! => nil))
|
||||
Capistrano::Upload.process([:s1, :s2], "test.txt", :data => "data")
|
||||
end
|
||||
|
||||
def test_process_when_sftp_open_fails_should_raise_error
|
||||
channel = mock("channel")
|
||||
channel.expects(:[]=).with(:done, true)
|
||||
|
|
Loading…
Add table
Reference in a new issue