forked from cloudfoundry-attic/stager
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Convert stager from fibers to threads
There are a couple of reasons for this change. Firstly, we don't need a massive amount of concurrency. Threads will serve us well enough and the resulting code is easier to read, maintain, and reason about. Secondly, this will ease the transition to using Warden containers for staging applications. I also took the opportunity to clean up some tests as I was refactoring. Test plan: - Unit tests pass (and are expanded). - BVTs pass - Manual testing using a script that executes parallel deploys succeeds. Change-Id: Ibb883a588de59d5d551bbd8e03261c45dd924580
- Loading branch information
mpage
committed
Apr 30, 2012
1 parent
45591af
commit 6a5a9d0
Showing
17 changed files
with
677 additions
and
466 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
module VCAP | ||
module Stager | ||
end | ||
end | ||
|
||
class VCAP::Stager::ProcessRunner | ||
MAX_READLEN = 1024 * 1024 | ||
|
||
def initialize(logger) | ||
@logger = logger | ||
end | ||
|
||
# Runs a command and captures stdout/stderr/status | ||
# | ||
# @param [String] cmd The command to run. | ||
# @param [Hash] opts | ||
# @option opts [Integer] :timeout How long the process is allowed to run for | ||
# | ||
# @return [Hash] A hash with the following keys: | ||
# :stdout => String | ||
# :stderr => String | ||
# :timed_out => Boolean | ||
# :status => Process::Status | ||
def run(cmd, opts = {}) | ||
pipes = [IO.pipe, IO.pipe] | ||
|
||
child_pid = Process.spawn(cmd, :out => pipes[0][1], :err => pipes[1][1]) | ||
|
||
# Only need the read side in parent | ||
pipes.each { |ios| ios[1].close } | ||
|
||
child_stdout, child_stderr = pipes[0][0], pipes[1][0] | ||
|
||
timeout = opts[:timeout] ? Float(opts[:timeout]) : nil | ||
|
||
# Holds data read thus far | ||
child_stdio_bufs = { | ||
child_stdout => "", | ||
child_stderr => "", | ||
} | ||
|
||
active = nil | ||
watched = child_stdio_bufs.keys | ||
start = Time.now | ||
|
||
while !watched.empty? && | ||
(active = IO.select(watched, nil, watched, timeout)) | ||
active.flatten.each do |io| | ||
begin | ||
child_stdio_bufs[io] << io.read_nonblock(MAX_READLEN) | ||
rescue IO::WaitReadable | ||
# Wait for more data | ||
rescue EOFError | ||
watched.delete(io) | ||
end | ||
end | ||
|
||
if timeout | ||
now = Time.now | ||
timeout -= now - start | ||
start = now | ||
end | ||
end | ||
|
||
ret = { | ||
:stdout => child_stdio_bufs[child_stdout], | ||
:stderr => child_stdio_bufs[child_stderr], | ||
:timed_out => active.nil?, | ||
:status => nil, | ||
} | ||
|
||
Process.kill("KILL", child_pid) if ret[:timed_out] | ||
|
||
Process.waitpid(child_pid) | ||
|
||
ret[:status] = $? | ||
|
||
ret | ||
ensure | ||
pipes.each do |ios| | ||
ios.each { |io| io.close unless io.closed? } | ||
end | ||
end | ||
|
||
# Runs the supplied command and logs the exit status, stdout, and stderr. | ||
# | ||
# @see VCAP::Stager::ProcessRunner#run for a description of arguments and | ||
# return value. | ||
def run_logged(cmd, opts = {}) | ||
ret = run(cmd, opts) | ||
|
||
exitstatus = ret[:status].exitstatus | ||
@logger.debug("Command #{cmd} exited with status #{exitstatus}") | ||
@logger.debug("stdout: #{ret[:stdout]}") | ||
@logger.debug("stderr: #{ret[:stderr]}") | ||
|
||
ret | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.