Class: Foreman::Engine
- Inherits:
-
Object
- Object
- Foreman::Engine
- Defined in:
- lib/foreman/engine.rb
Direct Known Subclasses
Defined Under Namespace
Classes: CLI
Constant Summary collapse
- HANDLED_SIGNALS =
The signals that the engine cares about.
[ :TERM, :INT, :HUP, :USR1, :USR2 ]
Instance Attribute Summary collapse
-
#env ⇒ Object
readonly
Returns the value of attribute env.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
-
#processes ⇒ Object
readonly
Returns the value of attribute processes.
Instance Method Summary collapse
-
#base_port ⇒ Object
Get the base port for this foreman instance.
-
#clear ⇒ Object
Clear the processes registered to this
Engine
. -
#each_process ⇒ Object
Yield each
Process
in order. -
#environment ⇒ Object
deprecated.
-
#formation ⇒ Object
Get the process formation.
-
#handle_hangup ⇒ Object
Handle a HUP signal.
-
#handle_interrupt ⇒ Object
Handle an INT signal.
-
#handle_signal(sig) ⇒ Object
Invoke the real handler for signal
sig
. - #handle_signal_forward(signal) ⇒ Object
-
#handle_term_signal ⇒ Object
Handle a TERM signal.
-
#initialize(options = {}) ⇒ Engine
constructor
Create an
Engine
for running processes. -
#kill_children(signal = "SIGTERM") ⇒ Object
Send a signal to all processes started by this
Engine
. -
#killall(signal = "SIGTERM") ⇒ Object
Send a signal to the whole process group.
-
#load_env(filename) ⇒ Object
Load a .env file into the
env
for thisEngine
. -
#load_procfile(filename) ⇒ Object
Register processes by reading a Procfile.
-
#notice_signal ⇒ Object
Wake the main thread up via the selfpipe when there’s a signal.
-
#port_for(process, instance, base = nil) ⇒ Object
Get the port for a given process and offset.
-
#process(name) ⇒ Object
Get the
Process
for a specifid name. -
#process_names ⇒ Object
List the available process names.
-
#register(name, command, options = {}) ⇒ Object
Register a process to be run by this
Engine
. -
#register_signal_handlers ⇒ Object
Set up deferred signal handlers.
-
#restore_default_signal_handlers ⇒ Object
Unregister deferred signal handlers.
-
#root ⇒ Object
Get the root directory for this
Engine
. -
#start ⇒ Object
Start the processes registered to this
Engine
.
Constructor Details
#initialize(options = {}) ⇒ Engine
Create an Engine
for running processes
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/foreman/engine.rb', line 27 def initialize(={}) @options = .dup @options[:formation] ||= "all=1" @options[:timeout] ||= 5 @env = {} @mutex = Mutex.new @names = {} @processes = [] @running = {} @readers = {} @shutdown = false # Self-pipe for deferred signal-handling (ala djb: http://cr.yp.to/docs/selfpipe.html) reader, writer = create_pipe reader.close_on_exec = true if reader.respond_to?(:close_on_exec) writer.close_on_exec = true if writer.respond_to?(:close_on_exec) @selfpipe = { :reader => reader, :writer => writer } # Set up a global signal queue # http://blog.rubybestpractices.com/posts/ewong/016-Implementing-Signal-Handlers.html Thread.main[:signal_queue] = [] end |
Instance Attribute Details
#env ⇒ Object (readonly)
Returns the value of attribute env.
15 16 17 |
# File 'lib/foreman/engine.rb', line 15 def env @env end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
16 17 18 |
# File 'lib/foreman/engine.rb', line 16 def @options end |
#processes ⇒ Object (readonly)
Returns the value of attribute processes.
17 18 19 |
# File 'lib/foreman/engine.rb', line 17 def processes @processes end |
Instance Method Details
#base_port ⇒ Object
Get the base port for this foreman instance
283 284 285 |
# File 'lib/foreman/engine.rb', line 283 def base_port ([:port] || env["PORT"] || ENV["PORT"] || 5000).to_i end |
#clear ⇒ Object
Clear the processes registered to this Engine
158 159 160 161 |
# File 'lib/foreman/engine.rb', line 158 def clear @names = {} @processes = [] end |
#each_process ⇒ Object
Yield each Process
in order
250 251 252 253 254 |
# File 'lib/foreman/engine.rb', line 250 def each_process process_names.each do |name| yield name, process(name) end end |
#environment ⇒ Object
deprecated
288 289 290 |
# File 'lib/foreman/engine.rb', line 288 def environment env end |
#formation ⇒ Object
Get the process formation
226 227 228 |
# File 'lib/foreman/engine.rb', line 226 def formation @formation ||= parse_formation([:formation]) end |
#handle_hangup ⇒ Object
Handle a HUP signal
130 131 132 133 |
# File 'lib/foreman/engine.rb', line 130 def handle_hangup system "SIGHUP received, starting shutdown" @shutdown = true end |
#handle_interrupt ⇒ Object
Handle an INT signal
123 124 125 126 |
# File 'lib/foreman/engine.rb', line 123 def handle_interrupt system "SIGINT received, starting shutdown" @shutdown = true end |
#handle_signal(sig) ⇒ Object
Invoke the real handler for signal sig
. This shouldn’t be called directly by signal handlers, as it might invoke code which isn’t re-entrant.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/foreman/engine.rb', line 99 def handle_signal(sig) case sig when :TERM handle_term_signal when :INT handle_interrupt when :HUP handle_hangup when *HANDLED_SIGNALS handle_signal_forward(sig) else system "unhandled signal #{sig}" end end |
#handle_signal_forward(signal) ⇒ Object
135 136 137 138 |
# File 'lib/foreman/engine.rb', line 135 def handle_signal_forward(signal) system "#{signal} received, forwarding it to children" kill_children signal end |
#handle_term_signal ⇒ Object
Handle a TERM signal
116 117 118 119 |
# File 'lib/foreman/engine.rb', line 116 def handle_term_signal system "SIGTERM received, starting shutdown" @shutdown = true end |
#kill_children(signal = "SIGTERM") ⇒ Object
Send a signal to all processes started by this Engine
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/foreman/engine.rb', line 189 def kill_children(signal="SIGTERM") if Foreman.windows? @running.each do |pid, (process, index)| system "sending #{signal} to #{name_for(pid)} at pid #{pid}" begin Process.kill(signal, pid) rescue Errno::ESRCH, Errno::EPERM end end else begin pids = @running.keys.compact Process.kill signal, *pids unless pids.empty? rescue Errno::ESRCH, Errno::EPERM end end end |
#killall(signal = "SIGTERM") ⇒ Object
Send a signal to the whole process group.
211 212 213 214 215 216 217 218 219 220 |
# File 'lib/foreman/engine.rb', line 211 def killall(signal="SIGTERM") if Foreman.windows? kill_children(signal) else begin Process.kill "-#{signal}", Process.pid rescue Errno::ESRCH, Errno::EPERM end end end |
#load_env(filename) ⇒ Object
Load a .env file into the env
for this Engine
179 180 181 182 183 |
# File 'lib/foreman/engine.rb', line 179 def load_env(filename) Foreman::Env.new(filename).entries do |name, value| @env[name] = value end end |
#load_procfile(filename) ⇒ Object
Register processes by reading a Procfile
167 168 169 170 171 172 173 |
# File 'lib/foreman/engine.rb', line 167 def load_procfile(filename) [:root] ||= File.dirname(filename) Foreman::Procfile.new(filename).entries do |name, command| register name, command, :cwd => [:root] end self end |
#notice_signal ⇒ Object
Wake the main thread up via the selfpipe when there’s a signal
85 86 87 88 89 90 91 92 |
# File 'lib/foreman/engine.rb', line 85 def notice_signal @selfpipe[:writer].write_nonblock( '.' ) rescue Errno::EAGAIN # Ignore writes that would block rescue Errno::EINTR # Retry if another signal arrived while writing retry end |
#port_for(process, instance, base = nil) ⇒ Object
Get the port for a given process and offset
271 272 273 274 275 276 277 |
# File 'lib/foreman/engine.rb', line 271 def port_for(process, instance, base=nil) if base base + (@processes.index(process.process) * 100) + (instance - 1) else base_port + (@processes.index(process) * 100) + (instance - 1) end end |
#process(name) ⇒ Object
Get the Process
for a specifid name
244 245 246 |
# File 'lib/foreman/engine.rb', line 244 def process(name) @names.invert[name] end |
#process_names ⇒ Object
List the available process names
234 235 236 |
# File 'lib/foreman/engine.rb', line 234 def process_names @processes.map { |p| @names[p] } end |
#register(name, command, options = {}) ⇒ Object
Register a process to be run by this Engine
148 149 150 151 152 153 154 |
# File 'lib/foreman/engine.rb', line 148 def register(name, command, ={}) [:env] ||= env [:cwd] ||= File.dirname(command.split(" ").first) process = Foreman::Process.new(command, ) @names[process] = name @processes << process end |
#register_signal_handlers ⇒ Object
Set up deferred signal handlers
67 68 69 70 71 72 73 |
# File 'lib/foreman/engine.rb', line 67 def register_signal_handlers HANDLED_SIGNALS.each do |sig| if ::Signal.list.include? sig.to_s trap(sig) { Thread.main[:signal_queue] << sig ; notice_signal } end end end |
#restore_default_signal_handlers ⇒ Object
Unregister deferred signal handlers
77 78 79 80 81 |
# File 'lib/foreman/engine.rb', line 77 def restore_default_signal_handlers HANDLED_SIGNALS.each do |sig| trap(sig, :DEFAULT) if ::Signal.list.include? sig.to_s end end |
#root ⇒ Object
Get the root directory for this Engine
260 261 262 |
# File 'lib/foreman/engine.rb', line 260 def root File.([:root] || Dir.pwd) end |
#start ⇒ Object
Start the processes registered to this Engine
54 55 56 57 58 59 60 61 62 63 |
# File 'lib/foreman/engine.rb', line 54 def start register_signal_handlers startup spawn_processes watch_for_output sleep 0.1 wait_for_shutdown_or_child_termination shutdown exit(@exitstatus) if @exitstatus end |