lib/foreman/engine.rb in foreman-0.27.0 vs lib/foreman/engine.rb in foreman-0.28.0.pre1

- old
+ new

@@ -21,130 +21,117 @@ def initialize(procfile, options={}) @procfile = Foreman::Procfile.new(procfile) @directory = File.expand_path(File.dirname(procfile)) @options = options @environment = read_environment_files(options[:env]) + @output_mutex = Mutex.new end def self.load_env!(env_file) @environment = read_environment_files(env_file) apply_environment! end def start proctitle "ruby: foreman master" - termtitle "#{File.basename(@directory)} - foreman (#{processes.size} processes)" + termtitle "#{File.basename(@directory)} - foreman" - processes.each do |process| - process.color = next_color - fork process - end - trap("TERM") { puts "SIGTERM received"; terminate_gracefully } trap("INT") { puts "SIGINT received"; terminate_gracefully } + assign_colors + spawn_processes + watch_for_output watch_for_termination end - def execute(name) - error "no such process: #{name}" unless process = procfile[name] - process.color = next_color - fork process - - trap("TERM") { puts "SIGTERM received"; terminate_gracefully } - trap("INT") { puts "SIGINT received"; terminate_gracefully } - - watch_for_termination - end - - def processes - procfile.processes - 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 private ###################################################################### - def fork(process) + def spawn_processes concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency]) - 1.upto(concurrency[process.name]) do |num| - fork_individual(process, num, port_for(process, num, @options[:port])) + procfile.entries.each do |entry| + reader, writer = IO.pipe + entry.spawn(concurrency[entry.name], writer, @directory, @environment, base_port).each do |process| + running_processes[process.pid] = process + readers[process] = reader + end end end - def fork_individual(process, num, port) - apply_environment! - - ENV["PORT"] = port.to_s - ENV["PS"] = "#{process.name}.#{num}" - - pid = Process.fork do - run(process) - end - - info "started with pid #{pid}", process - running_processes[pid] = process + def base_port + options[:port] || 5000 end - def run(process) - proctitle "ruby: foreman #{process.name}" - trap("SIGINT", "IGNORE") - - begin - Dir.chdir directory do - PTY.spawn(process.command) do |stdin, stdout, pid| - trap("SIGTERM") { Process.kill("SIGTERM", pid) } - until stdin.eof? - info stdin.gets, process - end - end - end - rescue PTY::ChildExited, Interrupt, Errno::EIO, Errno::ENOENT - begin - info "process exiting", process - rescue Interrupt - end - end - end - def kill_all(signal="SIGTERM") running_processes.each do |pid, process| Process.kill(signal, pid) rescue Errno::ESRCH end end def terminate_gracefully info "sending SIGTERM to all processes" kill_all "SIGTERM" - Timeout.timeout(3) { Process.waitall } + Timeout.timeout(5) { Process.waitall } rescue Timeout::Error info "sending SIGKILL to all processes" kill_all "SIGKILL" end + def watch_for_output + Thread.new do + begin + loop do + rs, ws = IO.select(readers.values, [], [], 1) + (rs || []).each do |r| + ps, message = r.gets.split(",", 2) + color = colors[ps.split(".").first] + info message, ps, color + end + end + rescue Exception => ex + puts ex.message + puts ex.backtrace + end + end + end + def watch_for_termination pid, status = Process.wait2 process = running_processes.delete(pid) - info "process terminated", process + info "process terminated", process.name terminate_gracefully kill_all rescue Errno::ECHILD end - def info(message, process=nil) - print process.color if process - print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(process)} | " + def info(message, name="system", color=Term::ANSIColor.white) + print color + print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | " print Term::ANSIColor.reset print message.chomp puts end + def print(message=nil) + @output_mutex.synchronize do + $stdout.print message + end + end + + def puts(message=nil) + @output_mutex.synchronize do + $stdout.puts message + end + end + def error(message) puts "ERROR: #{message}" exit 1 end @@ -154,13 +141,12 @@ longest = 6 if longest < 6 # system longest end end - def pad_process_name(process) - name = process ? "#{ENV["PS"]}" : "system" - name.ljust(longest_process_name + 3) # add 3 for process number padding + def pad_process_name(name="system") + name.to_s.ljust(longest_process_name + 3) # add 3 for process number padding end def proctitle(title) $0 = title end @@ -169,9 +155,27 @@ printf("\033]0;#{title}\007") end def running_processes @running_processes ||= {} + end + + def readers + @readers ||= {} + end + + def colors + @colors ||= {} + end + + def assign_colors + procfile.entries.each do |entry| + colors[entry.name] = next_color + end + end + + def process_by_reader(reader) + readers.invert[reader] end def next_color @current_color ||= -1 @current_color += 1