Recipe 13.13. Writing Custom Capistrano Tasks


Problem

You're using Capistrano to deploy your Rails application, but you find there's work to be done on your servers that is not covered by the default Capistrano tasks. You want a clean way to extend Capistrano to meet the specific needs of your deployment environment.

Solution

Create your own Capistrano tasks, or possibly a libraries of tasks for reuse across applications.

Think of a Capistrano task as a Ruby wrapper around a series of shell commands. That description gives you a good idea of the possibilities available to your custom tasks. In fact, that's exactly what Capistrano's run helper does; it lets you specify shell commands that run on your remote servers.

The following task is called clean_sessions; it simply executes a shell command to remove sessions that are older than two days.

desc "Removes old session files from /tmp" task :clean_sessions, :role => :app do    run "find /tmp/ruby_sess.* -ctime +2 -print | xargs rm -rf" end

The string passed to desc, preceding the task definition, serves as the task description when you display all defined Capistrano tasks. Immediately following that, the task method takes two arguments and a block. The first argument is the name of the task, in symbol form. The next argument is a list of the server roles to which the task applies. In this case, clean_sessions should only be run on the application servers. If this task should run on application and database servers, the :role option would be:

:role => [:db, :app]

Finally, task is passed a block of code containing Capistrano helper methods such as run, and even Ruby code.

Once you've defined a set of tasks, the next step is to make sure that they get loaded by your deployment recipe (e.g., /config/deploy.rb). The best way to do this is to create a file called cap_recipes.rb within your application's lib directory that defines your custom tasks. Now include that file into depoy.rb with a require statement:

                require 'lib/cap_recipes' set :application, "cookbook" set :repository, "https://svn.tupleshop.com/#{application}" role :web, "tupleshop.com" role :app, "tupleshop.com" set :user, "rob" set :deploy_to, "/var/www/#{application}"

You can reference Capistrano recipes on your filesystem that are common to several Rails applications. You can confirm that your tasks are being loaded and are available to the cap command by displaying all defined tasks:

cap show_tasks

This prints the name and description of all of the task definitions found by your deployment script.

To make sure that your tasks only run with the context of Capistrano, define all of your tasks within a block and pass that block to:

Capistrano.configuration(:must_exist).load { ... }

This statement requires that this file is included from within a Capistrano recipe. If it isn't, an exception is raised. The following file, cap_recipes.rb, defines two tasks within this protective construct:

lib/cap_recipes.rb:

Capistrano.configuration(:must_exist).load do   desc "Removes old session files from /tmp"   task :clean_sessions, :role => :app do      run "find /tmp/ruby_sess.* -ctime +2 -print | xargs rm -rf"   end   desc <<-DESC      Copy mongrel_cluster.yml to /etc/mongrel_cluster/,     named after your application (e.g.cookbook.yml).   DESC   task :link_mongrel_config, :role => :app do      sudo "mkdir -p /etc/mongrel_cluster"     sudo <<-CMD       ln -nfs /var/www/cookbook/current/config/mongrel_cluster.yml \         /etc/mongrel_cluster/#{application}.conf     CMD   end end

The second task in this file, link_mongrel_config, demonstrates another Capistrano helper method, sudo. This does much the same thing as run, but runs the commands on the remote servers as the superuser (root). sudo assumes that the user under which Capistrano is running is set up with root privileges in the remote systems sudo configuration file (e.g., /etc/sudoers).

Discussion

Capistrano provides several helper methods for doing work on your servers. Your tasks can contain Ruby code as well, but the helpers make doing remote work on your servers simple. Here's the complete list of Capistrano helpers:


run

Executes a POSIX shell command on all servers whose role is specified by the current task. The output of commands such as rails -v is printed to the terminal where the cap task was run.

If you want to interact with the output of a command, you can pass run a code block. If run is passed a block, the code in that blog is invoked for all output generated by the command. The block should accept three parameters: the SSH channel (which may be used to send data back to the remote process), the stream identifier (:err for stderr, and :out for stdout), and the data that was received.

As an example of interacting with command output, the following task watches all new content in the production.log on the application (:app) server:

desc "Watch the production log on the application server." task :watch_logs, :role => [:app] do   log_file = "#{shared_path}/log/production.log"   run "tail -f #{log_file}" do |channel, stream, data|     puts data if stream == :out      if stream == :err        puts "[Error: #{channel[:host]}] #{data}"       break     end        end end


sudo

Used like run, but uses sudo to execute commands on the remote server. The user who is running Capistrano must have sudo access.


put

Store the given data at the given location on all servers targeted by the current task. If :mode is specified, it is used to set the mode on the file.


delete

Deletes the given file from all servers targeted by the current task. If :recursive => true is specified, delete removes directories.


render

Renders an ERb template, and returns the result. This is useful for building documents to store on the remote servers. render("something", :foo => "hello") looks for something.rhtml in the current directory or in the capistrano/recipes/templates directory, and renders it with :foo defined as a local variable with the value "hello". render(:file => "something", :foo => "hello") does the same thing. render(:template => "<%= foo %> world", :foo => "hello") TReats the given string as an ERb template and renders it with the given hash of local variables.


transaction

Invokes a set of tasks in a transaction. If any task fails (raises an exception), all tasks executed within the transaction are inspected to see if they have an associated on_rollback hook, and if so, that hook is called.


on_rollback (& block)

Specifies an on_rollback hook for the currently executing task. If this or any subsequent task fails, and a transaction is active, this hook is executed.

Capistrano's default deploy task demonstrates how to use the transaction helper. The task wraps two other tasks, update_code and symlink, in a transaction block before calling the restart task:

desc <<-DESC A macro-task that updates the code, fixes the symlink, and restarts the application servers. DESC task :deploy do   transaction do     update_code     symlink   end   restart end

If update_code or symlink throw exceptions, the on_rollback hook that both of these tasks define is executed on all servers that run the deploy task. update_code defines the following on_rollback hook, which recursively deletes the release path:

desc <<-DESC Update all servers with the latest release of the source code. All this does is do a checkout (as defined by the selected scm module). DESC task :update_code, :roles => [:app, :db, :web] do   on_rollback { delete release_path, :recursive => true }   source.checkout(self)   run <<-CMD     rm -rf #{release_path}/log #{release_path}/public/system &&     ln -nfs #{shared_path}/log #{release_path}/log &&     ln -nfs #{shared_path}/system #{release_path}/public/system   CMD end

symlink also defines an on_rollback hook that recreates a symbolic link that points to the previous release on the server:

desc <<-DESC Update the 'current' symlink to point to the latest version of the application's code. DESC task :symlink, :roles => [:app, :db, :web] do   on_rollback { run "ln -nfs #{previous_release} #{current_path}" }   run "ln -nfs #{current_release} #{current_path}" end

See Also

  • Capistrano Manual, http://manuals.rubyonrails.com/read/book/17




Rails Cookbook
Rails Cookbook (Cookbooks (OReilly))
ISBN: 0596527314
EAN: 2147483647
Year: 2007
Pages: 250
Authors: Rob Orsini

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net