class Ziltoid::Process

Constants

ALLOWED_STATES
PREDOMINANT_STATES
WAIT_TIME_BEFORE_CHECK

Attributes

cpu_grace_time[RW]
cpu_limit[RW]
name[RW]
pid_file[RW]
ram_grace_time[RW]
ram_limit[RW]
restart_command[RW]
restart_grace_time[RW]
start_command[RW]
start_grace_time[RW]
stop_command[RW]
stop_grace_time[RW]

Public Class Methods

new(name, options = {}) click to toggle source
# File lib/ziltoid/process.rb, line 9
def initialize(name, options = {})
  self.name = name
  self.ram_limit = options[:limit] ? options[:limit][:ram] : nil
  self.cpu_limit = options[:limit] ? options[:limit][:cpu] : nil
  self.pid_file = options[:pid_file] || "~/.ziltoid/#{name}.pid"

  if options[:commands]
    self.start_command = options[:commands][:start] || nil
    self.stop_command = options[:commands][:stop] || nil
    self.restart_command = options[:commands][:restart] || nil
  end

  if options[:grace_times]
    self.start_grace_time = options[:grace_times][:start] || 0
    self.stop_grace_time = options[:grace_times][:stop] || 0
    self.restart_grace_time = options[:grace_times][:restart] || 0
    self.ram_grace_time = options[:grace_times][:ram] || 0
    self.cpu_grace_time = options[:grace_times][:cpu] || 0
  end
end

Public Instance Methods

above_cpu_limit?(include_children = true) click to toggle source
# File lib/ziltoid/process.rb, line 51
def above_cpu_limit?(include_children = true)
  Ziltoid::System.cpu_usage(self.pid, include_children) > self.cpu_limit.to_f
end
above_ram_limit?(include_children = true) click to toggle source
# File lib/ziltoid/process.rb, line 55
def above_ram_limit?(include_children = true)
  Ziltoid::System.ram_usage(self.pid, include_children) > self.ram_limit.to_i * 1024
end
alive?() click to toggle source
# File lib/ziltoid/process.rb, line 37
def alive?
  Ziltoid::System.pid_alive?(self.pid)
end
dead?() click to toggle source
# File lib/ziltoid/process.rb, line 41
def dead?
  !alive?
end
pending_grace_time?() click to toggle source
# File lib/ziltoid/process.rb, line 87
def pending_grace_time?
  current_state = self.state
  PREDOMINANT_STATES.include?(current_state) && self.updated_at.to_i > Time.now.to_i - self.send("#{current_state.gsub(/p?ed/, '')}_grace_time").to_i
end
pid() click to toggle source
# File lib/ziltoid/process.rb, line 30
def pid
  if self.pid_file && File.exist?(self.pid_file)
    str = File.read(pid_file)
    str.to_i if str.size > 0
  end
end
processable?(target_state) click to toggle source
# File lib/ziltoid/process.rb, line 69
def processable?(target_state)
  current_state = self.state
  # started, stopped and restarted are 'predominant' current states,
  # we never proceed unless the corresponding grace time is over
  Watcher.log("Current state : #{current_state} - updated_at : #{self.updated_at.to_i} - target_state : #{target_state}")
  return false if pending_grace_time?
  return true if PREDOMINANT_STATES.include?(target_state)

  # above_cpu_limit and above_ram_limit grace times are different,
  # they represent a time a process has to be in that state to actually be processed (restarted most likely)
  case target_state
  when "above_cpu_limit"
    current_state == target_state && self.updated_at.to_i < Time.now.to_i - self.cpu_grace_time.to_i
  when "above_ram_limit"
    current_state == target_state && self.updated_at.to_i < Time.now.to_i - self.ram_grace_time.to_i
  end
end
remove_pid_file() click to toggle source
# File lib/ziltoid/process.rb, line 45
def remove_pid_file
  if self.pid_file && File.exist?(self.pid_file)
    File.delete(self.pid_file)
  end
end
restart!() click to toggle source
# File lib/ziltoid/process.rb, line 166
def restart!
  return unless processable?("restarted")

  Watcher.log("Ziltoid is restarting process #{self.name}", Logger::WARN)
  alive = self.alive?

  if alive && self.restart_command
    update_state("restarted")
    return system("#{self.restart_command}")
  end

  stop! if alive
  return start!
end
start!() click to toggle source
# File lib/ziltoid/process.rb, line 126
def start!
  return if Ziltoid::System.pid_alive?(self.pid)
  return unless processable?("started")

  Watcher.log("Ziltoid is starting process #{self.name}", Logger::WARN)
  remove_pid_file
  system(self.start_command)
  update_state("started")
end
state() click to toggle source
# File lib/ziltoid/process.rb, line 59
def state
  state_hash = Ziltoid::Watcher.read_state[self.name]
  state_hash["state"] if state_hash
end
stop!() click to toggle source
# File lib/ziltoid/process.rb, line 136
def stop!
  return unless processable?("stopped")

  Watcher.log("Ziltoid is stoping process #{self.name}", Logger::WARN)
  memoized_pid = self.pid

  if dead?
    remove_pid_file
  else

    Thread.new do
      system(self.stop_command)
      sleep(WAIT_TIME_BEFORE_CHECK)
      if alive?
        system("kill #{memoized_pid}")
        sleep(WAIT_TIME_BEFORE_CHECK)
        if alive?
          system("kill -9 #{memoized_pid}")
          sleep(WAIT_TIME_BEFORE_CHECK)
        end
      end
      if dead?
        remove_pid_file
        update_state("stopped")
      end
    end.join

  end
end
update_state(state) click to toggle source
# File lib/ziltoid/process.rb, line 92
def update_state(state)
  process_states = Ziltoid::Watcher.read_state
  return nil unless ALLOWED_STATES.include?(state)
  memoized_process_state = process_states[self.name]

  process_states[self.name] = {
    "state" => state,
    "updated_at" => memoized_process_state && memoized_process_state["state"] == state ? memoized_process_state["updated_at"].to_i : Time.now.to_i
  }
  Ziltoid::Watcher.write_state(process_states)
end
updated_at() click to toggle source
# File lib/ziltoid/process.rb, line 64
def updated_at
  state_hash = Ziltoid::Watcher.read_state[self.name]
  state_hash["updated_at"] if state_hash
end
watch!() click to toggle source
# File lib/ziltoid/process.rb, line 104
def watch!
  Watcher.log("Ziltoid is watching process #{self.name}")
  if !alive?
    Watcher.log("Process #{self.name} is dead", Logger::WARN)
    return start!
  end
  if above_cpu_limit?
    update_state("above_cpu_limit") unless pending_grace_time?
    if processable?("above_cpu_limit")
      Watcher.log("Process #{self.name} is above CPU limit (#{self.cpu_limit.to_f})", Logger::WARN)
      return restart!
    end
  end
  if above_ram_limit?
    update_state("above_ram_limit") unless pending_grace_time?
    if processable?("above_ram_limit")
      Watcher.log("Process #{self.name} is above RAM limit (#{self.ram_limit.to_f})", Logger::WARN)
      return restart!
    end
  end
end