module Eye::Process::Commands

Public Instance Methods

restart_process() click to toggle source
# File lib/eye/process/commands.rb, line 66
def restart_process
  debug 'restart_process command'

  switch :restarting

  if self[:restart_command]
    execute_restart_command
    sleep_grace(:restart_grace)
    result = check_alive_with_refresh_pid_if_needed
    switch(result ? :restarted : :crashed)
  else
    stop_process
    start_process
  end

  true

rescue StateMachine::InvalidTransition, Eye::Process::StateError => e
  warn "wrong switch '#{e.message}'"
  nil
end
start_process() click to toggle source
# File lib/eye/process/commands.rb, line 3
def start_process
  debug 'start_process command'

  switch :starting

  unless self[:start_command]
    warn 'no :start_command found, unmonitoring'
    switch :unmonitoring, Eye::Reason.new(:no_start_command)
    return :no_start_command
  end

  result = self[:daemonize] ? daemonize_process : execute_process

  if !result[:error]
    debug "process <#{self.pid}> started successfully"
    switch :started
  else
    error "process <#{self.pid}> failed to start (#{result[:error].inspect})"

    if process_really_running?
      warn "killing <#{self.pid}> due to error"
      send_signal(:KILL)
      sleep 0.2 # little grace
    end

    self.pid = nil
    switch :crashed
  end

  result

rescue StateMachine::InvalidTransition, Eye::Process::StateError => e
  warn "wrong switch '#{e.message}'"

  :state_error
end
stop_process() click to toggle source
# File lib/eye/process/commands.rb, line 40
def stop_process
  debug 'stop_process command'

  switch :stopping

  kill_process

  if process_really_running?
    warn "process <#{self.pid}> was not stopped; try checking your command/signals or tuning the stop_timeout/stop_grace values"

    switch :unmonitoring, Eye::Reason.new(:'not stopped (soft command)')
    nil

  else
    switch :stopped

    clear_pid_file if self[:clear_pid] # by default for all

    true
  end

rescue StateMachine::InvalidTransition, Eye::Process::StateError => e
  warn "wrong switch '#{e.message}'"
  nil
end

Private Instance Methods

check_logs_str() click to toggle source
# File lib/eye/process/commands.rb, line 258
def check_logs_str
  if !self[:stdout] && !self[:stderr]
    'you may want to configure stdout/err/all logs for this process'
  else
    "you should check the process logs #{[self[:stdout], self[:stderr]]}"
  end
end
daemonize_process() click to toggle source
# File lib/eye/process/commands.rb, line 171
def daemonize_process
  time_before = Time.now
  res = Eye::System.daemonize(self[:start_command], config)
  start_time = Time.now - time_before

  info "daemonizing: `#{self[:start_command]}` with start_grace: #{self[:start_grace].to_f}s, env: '#{environment_string}', <#{res[:pid]}> (in #{self[:working_dir]})"

  if res[:error]

    if res[:error].message == 'Permission denied - open'
      error "daemonize failed with #{res[:error].inspect}; make sure #{[self[:stdout], self[:stderr]]} are writable"
    else
      error "daemonize failed with #{res[:error].inspect}"
    end

    return {:error => res[:error].inspect}
  end

  self.pid = res[:pid]

  unless self.pid
    error 'no pid was returned'
    return {:error => :empty_pid}
  end

  sleep_grace(:start_grace)

  unless process_really_running?
    error "process <#{self.pid}> not found, it may have crashed (#{check_logs_str})"
    return {:error => :not_really_running}
  end

  # if we using leaf child stratedy, pid should be used as last child process
  if self[:use_leaf_child]
    if lpid = Eye::SystemResources.leaf_child(self.pid)
      info "leaf child for <#{self.pid}> found: <#{lpid}>, accepting it!"
      self.parent_pid = self.pid
      self.pid = lpid
    else
      warn "leaf child not found for <#{self.pid}>, skipping it"
    end
  end

  if control_pid? && !failsafe_save_pid
    return {:error => :cant_write_pid}
  end

  res
end
execute_process() click to toggle source
# File lib/eye/process/commands.rb, line 221
def execute_process
  info "executing: `#{self[:start_command]}` with start_timeout: #{config[:start_timeout].to_f}s, start_grace: #{self[:start_grace].to_f}s, env: '#{environment_string}' (in #{self[:working_dir]})"
  time_before = Time.now

  res = execute(self[:start_command], config.merge(:timeout => config[:start_timeout]))
  start_time = Time.now - time_before

  if res[:error]

    if res[:error].message == 'Permission denied - open'
      error "execution failed with #{res[:error].inspect}; ensure that #{[self[:stdout], self[:stderr]]} are writable"
    elsif res[:error].class == Timeout::Error
      error "execution failed with #{res[:error].inspect}; try increasing the start_timeout value (the current value of #{self[:start_timeout]}s seems too short)"
    else
      error "execution failed with #{res[:error].inspect}"
    end

    return {:error => res[:error].inspect}
  end

  sleep_grace(:start_grace)

  unless set_pid_from_file
    error "exit status #{res[:exitstatus]}, pid_file (#{self[:pid_file_ex]}) did not appear within the start_grace period (#{self[:start_grace].to_f}s); check your start_command, or tune the start_grace value (eye expect process to create pid_file in self-daemonization mode)"
    return {:error => :pid_not_found}
  end

  unless process_really_running?
    error "exit status #{res[:exitstatus]}, process <#{self.pid}> (from #{self[:pid_file_ex]}) was not found; ensure that the pid_file is being updated correctly (#{check_logs_str})"
    return {:error => :not_really_running}
  end

  res[:pid] = self.pid
  info "exit status #{res[:exitstatus]}, process <#{res[:pid]}> (from #{self[:pid_file_ex]}) was found"
  res
end
execute_restart_command() click to toggle source
# File lib/eye/process/commands.rb, line 148
def execute_restart_command
  unless self.pid
    error 'cannot restart a process without a pid'
    return
  end

  cmd = prepare_command(self[:restart_command])
  info "executing: `#{cmd}` with restart_timeout: #{self[:restart_timeout].to_f}s and restart_grace: #{self[:restart_grace].to_f}s"

  res = execute(cmd, config.merge(:timeout => self[:restart_timeout]))

  if res[:error]

    if res[:error].class == Timeout::Error
      error "restart_command failed with #{res[:error].inspect}; try tuning the restart_timeout value"
    else
      error "restart_command failed with #{res[:error].inspect}"
    end
  end

  res
end
kill_process() click to toggle source
# File lib/eye/process/commands.rb, line 90
def kill_process
  unless self.pid
    error 'cannot stop a process without a pid'
    return
  end

  if self[:stop_command]
    cmd = prepare_command(self[:stop_command])
    info "executing: `#{cmd}` with stop_timeout: #{self[:stop_timeout].to_f}s and stop_grace: #{self[:stop_grace].to_f}s"
    res = execute(cmd, config.merge(:timeout => self[:stop_timeout]))

    if res[:error]

      if res[:error].class == Timeout::Error
        error "stop_command failed with #{res[:error].inspect}; try tuning the stop_timeout value"
      else
        error "stop_command failed with #{res[:error].inspect}"
      end
    end

    sleep_grace(:stop_grace)

  elsif self[:stop_signals]
    info "executing stop_signals #{self[:stop_signals].inspect}"
    stop_signals = self[:stop_signals].clone

    signal = stop_signals.shift
    send_signal(signal)

    while stop_signals.present?
      delay = stop_signals.shift
      signal = stop_signals.shift

      if wait_for_condition(delay.to_f, 0.3){ !process_really_running? }
        info 'has terminated'
        break
      end

      send_signal(signal) if signal
    end

    sleep_grace(:stop_grace)

  else # default command
    debug "executing: `kill -TERM #{self.pid}` with stop_grace: #{self[:stop_grace].to_f}s"
    send_signal(:TERM)

    sleep_grace(:stop_grace)

    # if process not die here, by default we force kill it
    if process_really_running?
      warn "process <#{self.pid}> did not die after TERM, sending KILL"
      send_signal(:KILL)
      sleep 0.1 # little grace
    end
  end
end
prepare_command(command) click to toggle source
# File lib/eye/process/commands.rb, line 266
def prepare_command(command)
  if self.pid
    command.to_s.gsub('{{PID}}', self.pid.to_s).gsub('{PID}', self.pid.to_s)
  else
    command
  end
end
sleep_grace(grace_name) click to toggle source
# File lib/eye/process/commands.rb, line 274
def sleep_grace(grace_name)
  grace = self[grace_name].to_f
  info "sleeping for :#{grace_name} #{grace}"
  sleep grace
end