Permalink
Browse files

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

Adding #stop method to Foreman::Engine
  • Loading branch information...
2 parents 69216b4 + 3a2a53b commit dbe51832b0c4862c834903d458ee7e93f66a7475 @ddollar ddollar committed Apr 11, 2012
Showing with 58 additions and 41 deletions.
  1. +1 −1 lib/foreman/cli.rb
  2. +45 −31 lib/foreman/engine.rb
  3. +1 −1 spec/foreman/cli_spec.rb
  4. +11 −8 spec/foreman/engine_spec.rb
View
@@ -30,7 +30,7 @@ def is_thor_reserved_word?(word, type)
def start(process=nil)
check_procfile!
engine.options[:concurrency] = "#{process}=1" if process
- engine.start
+ engine.run
end
desc "export FORMAT LOCATION", "Export the application to another process management format"
View
@@ -28,35 +28,28 @@ def initialize(procfile, options={})
@output_mutex = Mutex.new
end
- def start
+ def run
proctitle "ruby: foreman master"
termtitle "#{File.basename(@directory)} - foreman"
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
trap("INT") { puts "SIGINT received"; terminate_gracefully }
assign_colors
- spawn_processes
+ start
watch_for_output
watch_for_termination
+ terminate_gracefully
end
- def port_for(process, num, base_port=nil)
- base_port ||= 5000
- offset = procfile.process_names.index(process.name) * 100
- base_port.to_i + offset + num - 1
- end
-
- def apply_environment!
- environment.each { |k,v| ENV[k] = v }
- end
-
-private ######################################################################
-
- def spawn_processes
+ 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
@@ -65,32 +58,54 @@ def spawn_processes
end
end
- def base_port
- options[:port] || 5000
- end
-
- def kill_all(signal="SIGTERM")
+ def stop(name=nil, signal='SIGTERM')
running_processes.each do |pid, process|
- info "sending #{signal} to pid #{pid}"
+ 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
+ def port_for(process, num, base_port=nil)
+ base_port ||= 5000
+ offset = procfile.process_names.index(process.name) * 100
+ base_port.to_i + offset + num - 1
+ end
+
+ def apply_environment!
+ environment.each { |k,v| ENV[k] = v }
+ end
+
+private ######################################################################
+
+ def base_port
+ options[:port] || 5000
+ end
+
def terminate_gracefully
return if @terminating
@terminating = true
- info "sending SIGTERM to all processes"
- kill_all "SIGTERM"
Timeout.timeout(5) do
- while running_processes.length > 0
- pid, status = Process.wait2
- process = running_processes.delete(pid)
- info "process terminated", process.name
- end
+ stop
end
rescue Timeout::Error
- info "sending SIGKILL to all processes"
- kill_all "SIGKILL"
+ stop(nil, 'SIGKILL')
+ rescue Errno::ECHILD
end
def poll_readers
@@ -123,7 +138,6 @@ def watch_for_termination
pid, status = Process.wait2
process = running_processes.delete(pid)
info "process terminated", process.name
- terminate_gracefully
rescue Errno::ECHILD
end
View
@@ -21,7 +21,7 @@
it "runs successfully" do
dont_allow(subject).error
- mock.instance_of(Foreman::Engine).start
+ mock.instance_of(Foreman::Engine).run
subject.start
end
@@ -28,14 +28,15 @@
end
end
- describe "start" do
+ describe "run" do
it "forks the processes" do
write_procfile
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(subject).watch_for_output
mock(subject).watch_for_termination
- subject.start
+ mock(subject).terminate_gracefully
+ subject.run
end
it "handles concurrency" do
@@ -45,7 +46,8 @@
mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./bravo", is_a(IO)).never
mock(engine).watch_for_output
mock(engine).watch_for_termination
- engine.start
+ mock(engine).terminate_gracefully
+ engine.run
end
end
@@ -55,7 +57,7 @@
stub(Process).fork
any_instance_of(Foreman::Engine) do |engine|
stub(engine).info
- stub(engine).spawn_processes
+ stub(engine).start
stub(engine).watch_for_termination
end
end
@@ -64,15 +66,15 @@
File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
engine.environment.should == {"FOO"=>"baz"}
- engine.start
+ engine.run
end
it "should read more than one if specified" do
File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") }
File.open("/tmp/env2", "w") { |f| f.puts("BAZ=qux") }
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env1,/tmp/env2")
engine.environment.should == { "FOO"=>"bar", "BAZ"=>"qux" }
- engine.start
+ engine.run
end
it "should handle quoted values" do
@@ -95,7 +97,7 @@
File.open(".env", "w") { |f| f.puts("FOO=qoo") }
engine = Foreman::Engine.new("Procfile")
engine.environment.should == {"FOO"=>"qoo"}
- engine.start
+ engine.run
end
end
@@ -109,7 +111,8 @@
it "should spawn" do
stub(subject).watch_for_output
stub(subject).watch_for_termination
- subject.start
+ stub(subject).terminate_gracefully
+ subject.run
Process.waitall
mock(subject).info(/started with pid \d+/, "utf8.1", anything)
mock(subject).info("\xff\x03\n", "utf8.1", anything)

0 comments on commit dbe5183

Please sign in to comment.