Capistrano: Executing a command as root without using sudo

I don't have time to read and I want to jump immediately to the solution.

The problem

From the day I started using DelayedJob in combination with God, I faced the problem to restart the god group every time I deployed a new application release.

In fact, when a rake task (God runs DelayedJob using the rake jobs:work task, here's the config) is started, it loads the entire Rails environment and keeps it in memory as long as the process is running. When you deploy a new version, if you don't restart the rake task, DelayedJob continues to run with the previous Rails version environment.

To restart a task/group in God:

$ god restart GROUP_NAME

Speaking about my servers, the problem is that God runs as root while Capistrano deploys new releases using a not-root not-sudo user, for security purposes. In practice, it means I can't use the Capistrano sudo command because the deploy user is not in the sudoers list. Fortunately, the solution exists and is to run the command as su -c <command>.

The gotcha here is that when you use su you have to handle the remote server root password prompt. Thanks to Capistrano, this is as easy as pie.

Here's the method I created to run the command.

The solution

# Runs +command+ as root invoking the command with su -c
# and handling the root password prompt.
#
#   surun "/etc/init.d/apache reload"
#   # Executes
#   # su - -c '/etc/init.d/apache reload'
#
def surun(command)
  password = fetch(:root_password, Capistrano::CLI.password_prompt("root password: "))
  run("su - -c '#{command}'") do |channel, stream, output|
    channel.send_data("#{password}n") if output
  end
end

And here's the final Capistrano/DelayedJob/Run code fragment.

namespace :god do
  desc "Restart the god group 'GROUP_NAME'"
  task :restart, :roles => :app do
    surun "god restart GROUP_NAME"
  end
end

after "deploy:restart", "delayed_job:restart"