Merge pull request #176 from rtyler/feature/156-foreman-stop-command

Adding #stop method to Foreman::Engine
This commit is contained in:
David Dollar committed 2012-04-11 10:29:54 -07:00
commit dbe51832b0
4 files changed
+58 -41

No files matched your search

+1 -1
View File
@@ -30,7 +30,7 @@ class Foreman::CLI < Thor
def start(process=nil) def start(process=nil)
check_procfile! check_procfile!
engine.options[:concurrency] = "#{process}=1" if process engine.options[:concurrency] = "#{process}=1" if process
engine.start engine.run
end end
desc "export FORMAT LOCATION", "Export the application to another process management format" desc "export FORMAT LOCATION", "Export the application to another process management format"
+45 -31
View File
@@ -28,7 +28,7 @@ class Foreman::Engine
@output_mutex = Mutex.new @output_mutex = Mutex.new
end end
def start def run
proctitle "ruby: foreman master" proctitle "ruby: foreman master"
termtitle "#{File.basename(@directory)} - foreman" termtitle "#{File.basename(@directory)} - foreman"
@@ -36,9 +36,49 @@ class Foreman::Engine
trap("INT") { puts "SIGINT received"; terminate_gracefully } trap("INT") { puts "SIGINT received"; terminate_gracefully }
assign_colors assign_colors
spawn_processes start
watch_for_output watch_for_output
watch_for_termination watch_for_termination
terminate_gracefully
end
def start(name=nil)
concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
procfile.entries.each do |entry|
unless name == nil
next unless entry.name == name
end
reader, writer = (IO.method(:pipe).arity == 0 ? IO.pipe : IO.pipe("BINARY"))
entry.spawn(concurrency[entry.name], writer, @directory, @environment, port_for(entry, 1, base_port)).each do |process|
running_processes[process.pid] = process
readers[process] = reader
end
end
end
def stop(name=nil, signal='SIGTERM')
running_processes.each do |pid, process|
unless name == ALL_PROCESSES
# Comparing against process.entry.name instead of process.name to
# make sure we match the process name exactly for any/all
# concurrently running processes by this name
next unless process.entry.name == name
else
info "sending #{signal} to all processes"
end
process.kill signal
process = running_processes.delete(pid)
Timeout.timeout(5) do
begin
Process.waitpid(pid)
info "process terminated", process.name
rescue Errno::ECHILD
end
end
end
end end
def port_for(process, num, base_port=nil) def port_for(process, num, base_port=nil)
@@ -53,44 +93,19 @@ class Foreman::Engine
private ###################################################################### private ######################################################################
def spawn_processes
concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
procfile.entries.each do |entry|
reader, writer = (IO.method(:pipe).arity == 0 ? IO.pipe : IO.pipe("BINARY"))
entry.spawn(concurrency[entry.name], writer, @directory, @environment, port_for(entry, 1, base_port)).each do |process|
running_processes[process.pid] = process
readers[process] = reader
end
end
end
def base_port def base_port
options[:port] || 5000 options[:port] || 5000
end end
def kill_all(signal="SIGTERM")
running_processes.each do |pid, process|
info "sending #{signal} to pid #{pid}"
process.kill signal
end
end
def terminate_gracefully def terminate_gracefully
return if @terminating return if @terminating
@terminating = true @terminating = true
info "sending SIGTERM to all processes"
kill_all "SIGTERM"
Timeout.timeout(5) do Timeout.timeout(5) do
while running_processes.length > 0 stop
pid, status = Process.wait2
process = running_processes.delete(pid)
info "process terminated", process.name
end
end end
rescue Timeout::Error rescue Timeout::Error
info "sending SIGKILL to all processes" stop(nil, 'SIGKILL')
kill_all "SIGKILL" rescue Errno::ECHILD
end end
def poll_readers def poll_readers
@@ -123,7 +138,6 @@ private ######################################################################
pid, status = Process.wait2 pid, status = Process.wait2
process = running_processes.delete(pid) process = running_processes.delete(pid)
info "process terminated", process.name info "process terminated", process.name
terminate_gracefully
rescue Errno::ECHILD rescue Errno::ECHILD
end end
+1 -1
View File
@@ -21,7 +21,7 @@ describe "Foreman::CLI", :fakefs do
it "runs successfully" do it "runs successfully" do
dont_allow(subject).error dont_allow(subject).error
mock.instance_of(Foreman::Engine).start mock.instance_of(Foreman::Engine).run
subject.start subject.start
end end
+11 -8
View File
@@ -28,14 +28,15 @@ describe "Foreman::Engine", :fakefs do
end end
end end
describe "start" do describe "run" do
it "forks the processes" do it "forks the processes" do
write_procfile write_procfile
mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./alpha", is_a(IO)) mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./alpha", is_a(IO))
mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./bravo", is_a(IO)) mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./bravo", is_a(IO))
mock(subject).watch_for_output mock(subject).watch_for_output
mock(subject).watch_for_termination mock(subject).watch_for_termination
subject.start mock(subject).terminate_gracefully
subject.run
end end
it "handles concurrency" do it "handles concurrency" do
@@ -45,7 +46,8 @@ describe "Foreman::Engine", :fakefs do
mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./bravo", is_a(IO)).never mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./bravo", is_a(IO)).never
mock(engine).watch_for_output mock(engine).watch_for_output
mock(engine).watch_for_termination mock(engine).watch_for_termination
engine.start mock(engine).terminate_gracefully
engine.run
end end
end end
@@ -55,7 +57,7 @@ describe "Foreman::Engine", :fakefs do
stub(Process).fork stub(Process).fork
any_instance_of(Foreman::Engine) do |engine| any_instance_of(Foreman::Engine) do |engine|
stub(engine).info stub(engine).info
stub(engine).spawn_processes stub(engine).start
stub(engine).watch_for_termination stub(engine).watch_for_termination
end end
end end
@@ -64,7 +66,7 @@ describe "Foreman::Engine", :fakefs do
File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") } File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env") engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
engine.environment.should == {"FOO"=>"baz"} engine.environment.should == {"FOO"=>"baz"}
engine.start engine.run
end end
it "should read more than one if specified" do it "should read more than one if specified" do
@@ -72,7 +74,7 @@ describe "Foreman::Engine", :fakefs do
File.open("/tmp/env2", "w") { |f| f.puts("BAZ=qux") } File.open("/tmp/env2", "w") { |f| f.puts("BAZ=qux") }
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env1,/tmp/env2") engine = Foreman::Engine.new("Procfile", :env => "/tmp/env1,/tmp/env2")
engine.environment.should == { "FOO"=>"bar", "BAZ"=>"qux" } engine.environment.should == { "FOO"=>"bar", "BAZ"=>"qux" }
engine.start engine.run
end end
it "should handle quoted values" do it "should handle quoted values" do
@@ -95,7 +97,7 @@ describe "Foreman::Engine", :fakefs do
File.open(".env", "w") { |f| f.puts("FOO=qoo") } File.open(".env", "w") { |f| f.puts("FOO=qoo") }
engine = Foreman::Engine.new("Procfile") engine = Foreman::Engine.new("Procfile")
engine.environment.should == {"FOO"=>"qoo"} engine.environment.should == {"FOO"=>"qoo"}
engine.start engine.run
end end
end end
@@ -109,7 +111,8 @@ describe "Foreman::Engine", :fakefs do
it "should spawn" do it "should spawn" do
stub(subject).watch_for_output stub(subject).watch_for_output
stub(subject).watch_for_termination stub(subject).watch_for_termination
subject.start stub(subject).terminate_gracefully
subject.run
Process.waitall Process.waitall
mock(subject).info(/started with pid \d+/, "utf8.1", anything) mock(subject).info(/started with pid \d+/, "utf8.1", anything)
mock(subject).info("\xff\x03\n", "utf8.1", anything) mock(subject).info("\xff\x03\n", "utf8.1", anything)