Skip to content

Commit

Permalink
Enhance sudo helper to return command. Also make run helper use the s…
Browse files Browse the repository at this point in the history
…udo password detection callback, so that sudo can be used in more complex scenarios.
  • Loading branch information
jamis committed May 25, 2008
1 parent 461ca96 commit b45290e
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 20 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
@@ -1,5 +1,7 @@
*unreleased*

* Enhance the sudo helper so it can be used to return the command, instead of executing it [Jamis Buck]

* Revert "make sudo helper play nicely with complex command chains", since it broke stuff [Jamis Buck]

* Make set(:default_shell, false) work for not using a shell on a per-command basis [Ryan McGeary]
Expand Down
46 changes: 30 additions & 16 deletions lib/capistrano/configuration/actions/invocation.rb
Expand Up @@ -50,31 +50,45 @@ def run(cmd, options={}, &block)

options = add_default_command_options(options)

if cmd.include?(sudo)
block = sudo_behavior_callback(block)
end

execute_on_servers(options) do |servers|
targets = servers.map { |s| sessions[s] }
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.
# Returns the command string used by capistrano to invoke a comamnd via
# sudo.
#
# run "#{sudo :as => 'bob'} mkdir /path/to/dir"
#
# Also, this module accepts a <tt>:sudo</tt> configuration variable,
# It can also be invoked like #run, but executing the command via sudo.
# This assumes that the sudo password (if required) is the same as the
# password for logging in to the server.
#
# sudo "mkdir /path/to/dir"
#
# Also, this method understands 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"), "-p '#{sudo_prompt}'", user, command].compact.join(" ")

run(command, options, &sudo_behavior_callback(block))
def sudo(*parameters, &block)
options = parameters.last.is_a?(Hash) ? parameters.pop.dup : {}
command = parameters.first
user = options[:as] && "-u #{options.delete(:as)}"

sudo_command = [fetch(:sudo, "sudo"), "-p '#{sudo_prompt}'", user].compact.join(" ")

if command
command = sudo_command + " " + command
run(command, options, &block)
else
return sudo_command
end
end

# Returns a Proc object that defines the behavior of the sudo
Expand All @@ -90,13 +104,13 @@ def sudo_behavior_callback(fallback) #:nodoc:
Proc.new do |ch, stream, out|
if out =~ /^#{Regexp.escape(sudo_prompt)}/
ch.send_data "#{self[:password]}\n"
elsif out =~ /try again/
elsif out =~ /^Sorry, try again/
if prompt_host.nil? || prompt_host == ch[:server]
prompt_host = ch[:server]
logger.important out, "#{stream} :: #{ch[:server]}"
reset! :password
end
else
elsif fallback
fallback.call(ch, stream, out)
end
end
Expand Down
8 changes: 4 additions & 4 deletions test/configuration/actions/invocation_test.rb
Expand Up @@ -149,7 +149,7 @@ def test_sudo_behavior_callback_with_incorrect_password_on_first_prompt
ch.stubs(:[]).with(:host).returns("capistrano")
ch.stubs(:[]).with(:server).returns(server("capistrano"))
@config.expects(:reset!).with(:password)
@config.sudo_behavior_callback(nil)[ch, nil, "blah blah try again blah blah"]
@config.sudo_behavior_callback(nil)[ch, nil, "Sorry, try again."]
end

def test_sudo_behavior_callback_with_incorrect_password_on_subsequent_prompts
Expand All @@ -164,9 +164,9 @@ def test_sudo_behavior_callback_with_incorrect_password_on_subsequent_prompts

@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"]
callback[ch, nil, "Sorry, try again."]
callback[ch2, nil, "Sorry, try again."] # shouldn't call reset!
callback[ch, nil, "Sorry, try again."]
end

def test_sudo_behavior_callback_should_defer_to_fallback_for_other_output
Expand Down

0 comments on commit b45290e

Please sign in to comment.