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
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.
-
#should_be_running? ⇒ Boolean
Should this process be running?.
-
#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
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
321 322 323 324 325 326 327 328 |
# File 'lib/procodile/instance.rb', line 321 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.
351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/procodile/instance.rb', line 351 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?
303 304 305 |
# File 'lib/procodile/instance.rb', line 303 def can_respawn? !stopping? && (respawns + 1) <= @process.max_respawns end |
#check(options = {}) ⇒ Object
Check the status of this process and handle as appropriate.
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/procodile/instance.rb', line 270 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
58 59 60 61 62 63 64 65 66 |
# File 'lib/procodile/instance.rb', line 58 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?
178 179 180 |
# File 'lib/procodile/instance.rb', line 178 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
201 202 203 204 205 |
# File 'lib/procodile/instance.rb', line 201 def on_stop @started_at = nil @stopped = true tidy end |
#pid_file_path ⇒ Object
Return the path to this instance’s PID file
71 72 73 |
# File 'lib/procodile/instance.rb', line 71 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
78 79 80 81 82 83 84 85 |
# File 'lib/procodile/instance.rb', line 78 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?
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
# File 'lib/procodile/instance.rb', line 368 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
310 311 312 313 314 315 316 |
# File 'lib/procodile/instance.rb', line 310 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
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/procodile/instance.rb', line 218 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 new_instance = @process.create_instance(@supervisor) new_instance.port = self.port new_instance.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) new_instance.port = self.port 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
90 91 92 93 94 95 96 97 98 |
# File 'lib/procodile/instance.rb', line 90 def running? if @pid ::Process.getpgid(@pid) ? true : false else false end rescue Errno::ESRCH false end |
#should_be_running? ⇒ Boolean
Should this process be running?
47 48 49 50 51 52 53 |
# File 'lib/procodile/instance.rb', line 47 def should_be_running? if stopped? || stopping? false else true end end |
#start ⇒ Object
Start a new instance of this process
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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/procodile/instance.rb', line 103 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 elsif @process.allocate_port_from && @process.restart_mode != 'start-term' # Allocate ports to this process sequentially from the starting port allocated_ports = (@supervisor.processes[@process] ? @supervisor.processes[@process].select(&:running?) : []).map(&:port) proposed_port = @process.allocate_port_from until @port unless allocated_ports.include?(proposed_port) @port = proposed_port end proposed_port += 1 end 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.
186 187 188 189 190 191 192 193 194 195 |
# File 'lib/procodile/instance.rb', line 186 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?
171 172 173 |
# File 'lib/procodile/instance.rb', line 171 def stopped? @stopped || false end |
#stopping? ⇒ Boolean
Is this instance supposed to be stopping/be stopped?
164 165 166 |
# File 'lib/procodile/instance.rb', line 164 def stopping? @stopping ? true : false end |
#tidy ⇒ Object
Tidy up when this process isn’t needed any more
210 211 212 213 |
# File 'lib/procodile/instance.rb', line 210 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
333 334 335 336 337 338 339 340 341 342 343 344 |
# File 'lib/procodile/instance.rb', line 333 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.
255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/procodile/instance.rb', line 255 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
391 392 393 394 395 396 397 398 399 400 401 402 403 |
# File 'lib/procodile/instance.rb', line 391 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 |