Skip to content

Latest commit

 

History

History
65 lines (38 loc) · 4.06 KB

2.x-DSL-Action-Invocation-Parallel.md

File metadata and controls

65 lines (38 loc) · 4.06 KB

Definition

parallel(options={}, &block)

Module

Capistrano::Configuration::Actions::Invocation

People frequently email the Capistrano mailing list, trying to find out how to tell which server Capistrano is on when it executes a command. They want to know this because they want to do one thing on server A, but another on server B. (The answer is that you can't tell which server is Capistrano is on, because it's on all of them at once--the run helper executes on all matching servers simultaneously.)

One way to do what they want is to execute the two commands serially: first, run the command on server A, and then run the other command on server B. This works, but it's slower because the two commands aren't running in parallel.

Enter the parallel helper. It lets you execute multiple (different) commands on multiple servers, in parallel. (Ahem. Hence the name.)

Usage

When invoked, it yields a helper object to the block. This object then lets you specify different conditions and the commands to execute if the conditions are true. You can also specify a fallback command, to execute on servers for which none of the other conditions matched.

At it's simplest, it works like this:

parallel do |session|
  session.when "in?(:app)", "/u/apps/social/script/restart-mongrel"
  session.when "in?(:web)", "/u/apps/social/script/restart-apache"
  session.else "echo nothing to do"
end

The above would run the "restart-mongrel" script on every server in the :app role, restart-apache on every server in the :web role, and would simply echo "nothing to do" on any other server. Also, servers that match more than one condition will have all matching conditions executed in parallel on them; in other words, a server in both the :app and :web roles would have both restart-mongrel and restart-apache invoked on them.

Arguments

The parallel helper accepts the same arguments as run, although not all make sense (e.g. :once). The options given will apply to every branch of the parallel call.

Conditions

Each when invocation takes a condition string. This string may be any valid Ruby expression, but there is one method and two variables that are exposed especially for access in these condition strings.

in?(role)

This will return true if the server currently being evaluated exists in the given role, and false otherwise.

** server **

This is a reference to the Capistrano::ServerDefinition object for the current server. From this you can access the host name (server.host) and other attributes of the server (such as server.options, to reference the options hash from the server's role.

** configuration **

This refers to the current Capistrano::Configuration instance, which you can use to access variables and so forth.

You can combine these (and other conditions) into arbitrarily complex conditions.

parallel do |session|
  session.when "in?(:app) || in?(:web)", "#{sudo} touch /path/to/lock.file"
  session.when "server.host =~ /db/ || server.host =~ /backup/", "scp /path/to/$CAPISTRANO:HOST$-backup.tar.gz remote.host:/path"
  session.when "configuration[:special_hosts].include?(server.host)", "echo special host"
end

Callbacks

Each condition (including the "else" condition) is basically a run invocation in miniature (although options may only be specified at the top level, as arguments to parallel, and not for each separate condition). Just as with run, you can specify a block to handle output and interaction, and you can do so for each condition. Also, the sudo helper can be embedded in any commands, just as with run.

session.when "in?(:app)", "tail -f /u/apps/social/log/access.log" do |channel, stream, data|
  puts "from tail on #{channel[:host]} (#{stream}): #{data}"
end

Because each condition can have it's own callback block, this lets you (potentially) configure multiple different state machines that run at the same time, reading and responding to output from the commands. Of course, as with run, if you leave off the block, the output from the command will simply be echoed to your terminal.