This class encapsulates a single command to be executed on a set of remote machines, in parallel.
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.
# File lib/capistrano/command.rb, line 146 146: def initialize(tree, sessions, options={}, &block) 147: if String === tree 148: tree = Tree.new(nil) { |t| t.else(tree, &block) } 149: elsif block 150: raise ArgumentError, "block given with tree argument" 151: end 152: 153: @tree = tree 154: @sessions = sessions 155: @options = options 156: @channels = open_channels 157: 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 Capistrano::CommandError.
# File lib/capistrano/command.rb, line 162 162: def process! 163: elapsed = Benchmark.realtime do 164: loop do 165: break unless process_iteration { @channels.any? { |ch| !ch[:closed] } } 166: end 167: end 168: 169: logger.trace "command finished in #{(elapsed * 1000).round}ms" if logger 170: 171: if (failed = @channels.select { |ch| ch[:status] != 0 }).any? 172: commands = failed.inject({}) { |map, ch| (map[ch[:command]] ||= []) << ch[:server]; map } 173: message = commands.map { |command, list| "#{command.inspect} on #{list.join(',')}" }.join("; ") 174: error = CommandError.new("failed: #{message}") 175: error.hosts = commands.values.flatten 176: raise error 177: end 178: 179: self 180: end
prepare a space-separated sequence of variables assignments intended to be prepended to a command, so the shell sets the environment before running the command. i.e.: options[:env] = {‘PATH’ => ‘/opt/ruby/bin:$PATH’,
'TEST' => '( "quoted" )'}
environment returns: “env TEST=(\ "quoted"\ ) PATH=/opt/ruby/bin:$PATH“
# File lib/capistrano/command.rb, line 277 277: def environment 278: return if options[:env].nil? || options[:env].empty? 279: @environment ||= if String === options[:env] 280: "env #{options[:env]}" 281: else 282: options[:env].inject("env") do |string, (name, value)| 283: value = value.to_s.gsub(/[ "]/) { |m| "\\#{m}" } 284: string << " #{name}=#{value}" 285: end 286: end 287: end
# File lib/capistrano/command.rb, line 192 192: def logger 193: options[:logger] 194: end
# File lib/capistrano/command.rb, line 196 196: def open_channels 197: sessions.map do |session| 198: server = session.xserver 199: tree.branches_for(server).map do |branch| 200: session.open_channel do |channel| 201: channel[:server] = server 202: channel[:host] = server.host 203: channel[:options] = options 204: channel[:branch] = branch 205: 206: request_pty_if_necessary(channel) do |ch, success| 207: if success 208: logger.trace "executing command", ch[:server] if logger 209: cmd = replace_placeholders(channel[:branch].command, ch) 210: 211: if options[:shell] == false 212: shell = nil 213: else 214: shell = "#{options[:shell] || "sh"} -c" 215: cmd = cmd.gsub(/'/) { |m| "'\\''" } 216: cmd = "'#{cmd}'" 217: end 218: 219: command_line = [environment, shell, cmd].compact.join(" ") 220: ch[:command] = command_line 221: 222: ch.exec(command_line) 223: ch.send_data(options[:data]) if options[:data] 224: else 225: # just log it, don't actually raise an exception, since the 226: # process method will see that the status is not zero and will 227: # raise an exception then. 228: logger.important "could not open channel", ch[:server] if logger 229: ch.close 230: end 231: end 232: 233: channel.on_data do |ch, data| 234: ch[:branch].callback[ch, :out, data] 235: end 236: 237: channel.on_extended_data do |ch, type, data| 238: ch[:branch].callback[ch, :err, data] 239: end 240: 241: channel.on_request("exit-status") do |ch, data| 242: ch[:status] = data.read_long 243: end 244: 245: channel.on_close do |ch| 246: ch[:closed] = true 247: end 248: end 249: end 250: end.flatten 251: end
# File lib/capistrano/command.rb, line 263 263: def replace_placeholders(command, channel) 264: roles = @tree.configuration && @tree.configuration.role_names_for_host(channel[:server]) 265: command = command.gsub(/\$CAPISTRANO:HOST\$/, channel[:host]) 266: command.gsub!(/\$CAPISTRANO:HOSTROLES\$/, roles.join(',')) if roles 267: command 268: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.