Class: Procodile::Instance
- Inherits:
-
Object
- Object
- Procodile::Instance
- Defined in:
- lib/procodile/instance.rb
Instance Attribute Summary collapse
-
#id ⇒ Object
readonly
Returns the value of attribute id.
-
#pid ⇒ Object
Returns the value of attribute pid.
-
#port ⇒ Object
readonly
Returns the value of attribute port.
-
#process ⇒ Object
Returns the value of attribute process.
-
#tag ⇒ Object
readonly
Returns the value of attribute tag.
Instance Method Summary collapse
-
#add_respawn ⇒ Object
Increment the counter of respawns for this process.
-
#allocate_port(max_attempts = 10) ⇒ Object
Find a port number for this instance to listen on.
-
#can_respawn? ⇒ Boolean
Can this process be respawned if needed?.
-
#check(options = {}) ⇒ Object
Check the status of this process and handle as appropriate.
-
#description ⇒ Object
Return a description for this instance.
-
#environment_variables ⇒ Object
Return an array of environment variables that should be set.
-
#failed? ⇒ Boolean
Has this failed?.
-
#initialize(supervisor, process, id) ⇒ Instance
constructor
A new instance of Instance.
-
#on_stop ⇒ Object
A method that will be called when this instance has been stopped and it isn’t going to be started again.
-
#pid_file_path ⇒ Object
Return the path to this instance’s PID file.
-
#pid_from_file ⇒ Object
Return the PID that is in the instances process PID file.
-
#port_available?(port) ⇒ Boolean
Is the given port available?.
-
#respawns ⇒ Object
Return the number of times this process has been respawned in the last hour.
-
#restart ⇒ Object
Retarts the process using the appropriate method from the process configuraiton.
-
#running? ⇒ Boolean
Is this process running? Pass an option to check the given PID instead of the instance.
-
#start ⇒ Object
Start a new instance of this process.
-
#status ⇒ Object
Return the status of this instance.
-
#stop ⇒ Object
Send this signal the signal to stop and mark the instance in a state that tells us that we want it to be stopped.
-
#stopped? ⇒ Boolean
Is this stopped?.
-
#stopping? ⇒ Boolean
Is this instance supposed to be stopping/be stopped?.
-
#tidy ⇒ Object
Tidy up when this process isn’t needed any more.
-
#to_hash ⇒ Object
Return this instance as a hash.
-
#update_pid ⇒ Object
Update the locally cached PID from that stored on the file system.
-
#without_rbenv(&block) ⇒ Object
If procodile is executed through rbenv it will pollute our environment which means that any spawned processes will be invoked with procodile’s ruby rather than the ruby that the application wishes to use.
Constructor Details
#initialize(supervisor, process, id) ⇒ Instance
Returns a new instance of Instance.
12 13 14 15 16 17 18 |
# File 'lib/procodile/instance.rb', line 12 def initialize(supervisor, process, id) @supervisor = supervisor @process = process @id = id @respawns = 0 @started_at = nil end |
Instance Attribute Details
#id ⇒ Object (readonly)
Returns the value of attribute id.
7 8 9 |
# File 'lib/procodile/instance.rb', line 7 def id @id end |
#pid ⇒ Object
Returns the value of attribute pid.
6 7 8 |
# File 'lib/procodile/instance.rb', line 6 def pid @pid end |
#port ⇒ Object (readonly)
Returns the value of attribute port.
10 11 12 |
# File 'lib/procodile/instance.rb', line 10 def port @port end |
#process ⇒ Object
Returns the value of attribute process.
8 9 10 |
# File 'lib/procodile/instance.rb', line 8 def process @process end |
#tag ⇒ Object (readonly)
Returns the value of attribute tag.
9 10 11 |
# File 'lib/procodile/instance.rb', line 9 def tag @tag end |
Instance Method Details
#add_respawn ⇒ Object
Increment the counter of respawns for this process
301 302 303 304 305 306 307 308 |
# File 'lib/procodile/instance.rb', line 301 def add_respawn if @last_respawn && @last_respawn < (Time.now - @process.respawn_window) @respawns = 1 else @last_respawn = Time.now @respawns += 1 end end |
#allocate_port(max_attempts = 10) ⇒ Object
Find a port number for this instance to listen on. We just check that nothing is already listening on it. The process is expected to take it straight away if it wants it.
331 332 333 334 335 336 337 338 339 340 341 342 343 |
# File 'lib/procodile/instance.rb', line 331 def allocate_port(max_attempts = 10) attempts = 0 until @port attempts += 1 possible_port = rand(10000) + 20000 if self.port_available?(possible_port) Procodile.log(@process.log_color, description, "Allocated port as #{possible_port}") return @port = possible_port elsif attempts >= max_attempts raise Procodile::Error, "Couldn't allocate port for #{instance.name}" end end end |
#can_respawn? ⇒ Boolean
Can this process be respawned if needed?
283 284 285 |
# File 'lib/procodile/instance.rb', line 283 def can_respawn? !stopping? && (respawns + 1) <= @process.max_respawns end |
#check(options = {}) ⇒ Object
Check the status of this process and handle as appropriate.
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/procodile/instance.rb', line 250 def check( = {}) return if failed? if self.running? # Everything is OK. The process is running. true else # If the process isn't running any more, update the PID in our memory from # the file in case the process has changed itself. return check if update_pid if @supervisor.allow_respawning? if can_respawn? Procodile.log(@process.log_color, description, "Process has stopped. Respawning...") start add_respawn elsif respawns >= @process.max_respawns Procodile.log(@process.log_color, description, "\e[41;37mWarning:\e[0m\e[31m this process has been respawned #{respawns} times and keeps dying.\e[0m") Procodile.log(@process.log_color, description, "It will not be respawned automatically any longer and will no longer be managed.".color(31)) @failed = Time.now tidy end else Procodile.log(@process.log_color, description, "Process has stopped. Respawning not available.") @failed = Time.now tidy end end end |
#description ⇒ Object
Return a description for this instance
23 24 25 |
# File 'lib/procodile/instance.rb', line 23 def description "#{@process.name}.#{@id}" end |
#environment_variables ⇒ Object
Return an array of environment variables that should be set
47 48 49 50 51 52 53 54 55 |
# File 'lib/procodile/instance.rb', line 47 def environment_variables vars = @process.environment_variables.merge({ 'PROC_NAME' => self.description, 'PID_FILE' => self.pid_file_path, 'APP_ROOT' => @process.config.root }) vars['PORT'] = @port.to_s if @port vars end |
#failed? ⇒ Boolean
Has this failed?
161 162 163 |
# File 'lib/procodile/instance.rb', line 161 def failed? @failed ? true : false end |
#on_stop ⇒ Object
A method that will be called when this instance has been stopped and it isn’t going to be started again
184 185 186 187 188 |
# File 'lib/procodile/instance.rb', line 184 def on_stop @started_at = nil @stopped = true tidy end |
#pid_file_path ⇒ Object
Return the path to this instance’s PID file
60 61 62 |
# File 'lib/procodile/instance.rb', line 60 def pid_file_path File.join(@process.config.pid_root, "#{description}.pid") end |
#pid_from_file ⇒ Object
Return the PID that is in the instances process PID file
67 68 69 70 71 72 73 74 |
# File 'lib/procodile/instance.rb', line 67 def pid_from_file if File.exist?(pid_file_path) pid = File.read(pid_file_path) pid.length > 0 ? pid.strip.to_i : nil else nil end end |
#port_available?(port) ⇒ Boolean
Is the given port available?
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/procodile/instance.rb', line 348 def port_available?(port) case @process.network_protocol when 'tcp' server = TCPServer.new('127.0.0.1', port) server.close true when 'udp' server = UDPSocket.new server.bind('127.0.0.1', port) server.close true else raise Procodile::Error, "Invalid network_protocol '#{@process.network_protocol}'" end rescue Errno::EADDRINUSE => e false end |
#respawns ⇒ Object
Return the number of times this process has been respawned in the last hour
290 291 292 293 294 295 296 |
# File 'lib/procodile/instance.rb', line 290 def respawns if @respawns.nil? || @last_respawn.nil? || @last_respawn < (Time.now - @process.respawn_window) 0 else @respawns end end |
#restart ⇒ Object
Retarts the process using the appropriate method from the process configuraiton
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/procodile/instance.rb', line 201 def restart Procodile.log(@process.log_color, description, "Restarting using #{@process.restart_mode} mode") update_pid case @process.restart_mode when 'usr1', 'usr2' if running? ::Process.kill(@process.restart_mode.upcase, @pid) @tag = @supervisor.tag if @supervisor.tag Procodile.log(@process.log_color, description, "Sent #{@process.restart_mode.upcase} signal to process #{@pid}") else Procodile.log(@process.log_color, description, "Process not running already. Starting it.") on_stop @process.create_instance(@supervisor).start end self when 'start-term' new_instance = @process.create_instance(@supervisor) new_instance.start stop new_instance when 'term-start' stop new_instance = @process.create_instance(@supervisor) Thread.new do sleep 0.5 while running? new_instance.start end new_instance end end |
#running? ⇒ Boolean
Is this process running? Pass an option to check the given PID instead of the instance
79 80 81 82 83 84 85 86 87 |
# File 'lib/procodile/instance.rb', line 79 def running? if @pid ::Process.getpgid(@pid) ? true : false else false end rescue Errno::ESRCH false end |
#start ⇒ Object
Start a new instance of this process
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/procodile/instance.rb', line 92 def start if stopping? Procodile.log(@process.log_color, description, "Process is stopped/stopping therefore cannot be started again.") return false end update_pid if running? Procodile.log(@process.log_color, description, "Already running with PID #{@pid}") nil else if @supervisor.[:port_allocations] && chosen_port = @supervisor.[:port_allocations][@process.name] if chosen_port == 0 allocate_port else @port = chosen_port Procodile.log(@process.log_color, description, "Assigned #{chosen_port} to process") end elsif @process.proxy? && @supervisor.tcp_proxy # Allocate a port randomly if a proxy is needed allocate_port end if (@supervisor.[:allocate_ports] && @supervisor.[:allocate_ports].include?(@process.name)) || (@process.proxy? && @supervisor.tcp_proxy) allocate_port end if self.process.log_path && @supervisor.[:force_single_log] != true log_destination = File.open(self.process.log_path, 'a') io = nil else reader, writer = IO.pipe log_destination = writer io = reader end @tag = @supervisor.tag.dup if @supervisor.tag Dir.chdir(@process.config.root) without_rbenv do @pid = ::Process.spawn(environment_variables, @process.command, :out => log_destination, :err => log_destination, :pgroup => true) end log_destination.close File.open(pid_file_path, 'w') { |f| f.write(@pid.to_s + "\n") } @supervisor.add_instance(self, io) ::Process.detach(@pid) Procodile.log(@process.log_color, description, "Started with PID #{@pid}" + (@tag ? " (tagged with #{@tag})" : '')) if self.process.log_path && io.nil? Procodile.log(@process.log_color, description, "Logging to #{self.process.log_path}") end @started_at = Time.now end end |
#status ⇒ Object
Return the status of this instance
30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/procodile/instance.rb', line 30 def status if stopped? 'Stopped' elsif stopping? 'Stopping' elsif running? 'Running' elsif failed? 'Failed' else 'Unknown' end end |
#stop ⇒ Object
Send this signal the signal to stop and mark the instance in a state that tells us that we want it to be stopped.
169 170 171 172 173 174 175 176 177 178 |
# File 'lib/procodile/instance.rb', line 169 def stop @stopping = Time.now update_pid if self.running? Procodile.log(@process.log_color, description, "Sending #{@process.term_signal} to #{@pid}") ::Process.kill(@process.term_signal, pid) else Procodile.log(@process.log_color, description, "Process already stopped") end end |
#stopped? ⇒ Boolean
Is this stopped?
154 155 156 |
# File 'lib/procodile/instance.rb', line 154 def stopped? @stopped || false end |
#stopping? ⇒ Boolean
Is this instance supposed to be stopping/be stopped?
147 148 149 |
# File 'lib/procodile/instance.rb', line 147 def stopping? @stopping ? true : false end |
#tidy ⇒ Object
Tidy up when this process isn’t needed any more
193 194 195 196 |
# File 'lib/procodile/instance.rb', line 193 def tidy FileUtils.rm_f(self.pid_file_path) Procodile.log(@process.log_color, description, "Removed PID file") end |
#to_hash ⇒ Object
Return this instance as a hash
313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/procodile/instance.rb', line 313 def to_hash { :description => self.description, :pid => self.pid, :respawns => self.respawns, :status => self.status, :running => self.running?, :started_at => @started_at ? @started_at.to_i : nil, :tag => self.tag, :port => @port } end |
#update_pid ⇒ Object
Update the locally cached PID from that stored on the file system.
235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/procodile/instance.rb', line 235 def update_pid pid_from_file = self.pid_from_file if pid_from_file && pid_from_file != @pid @pid = pid_from_file @started_at = File.mtime(self.pid_file_path) Procodile.log(@process.log_color, description, "PID file changed. Updated pid to #{@pid}") true else false end end |
#without_rbenv(&block) ⇒ Object
If procodile is executed through rbenv it will pollute our environment which means that any spawned processes will be invoked with procodile’s ruby rather than the ruby that the application wishes to use
371 372 373 374 375 376 377 378 379 380 381 382 383 |
# File 'lib/procodile/instance.rb', line 371 def without_rbenv(&block) previous_environment = ENV.select { |k,v| k =~ /\A(RBENV\_)/ } if previous_environment.size > 0 previous_environment.each { |key, value| ENV[key] = nil } previous_environment['PATH'] = ENV['PATH'] ENV['PATH'] = ENV['PATH'].split(':').select { |p| !(p =~ /\.rbenv\/versions/) }.join(':') end yield ensure previous_environment.each do |key, value| ENV[key] = value end end |