Class: Blessing::Runner

Inherits:
Object
  • Object
show all
Defined in:
lib/blessing/runner.rb

Overview

This class holds all handles to a specific Unicorn instance and through this that Unicorn is manipulated

Constant Summary collapse

DEFAULT_OPTS =
{
  :unicorn => `which unicorn`,  # Where is unicorn binary
  :max_restarts => 5,           # How many times to retry restarting
  :retry_delay => 1,            # How long (secs) sleep between retries
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(conf, opts = {}) ⇒ Runner

Returns a new instance of Runner.



18
19
20
21
22
23
24
# File 'lib/blessing/runner.rb', line 18

def initialize conf, opts = {}
  @leader = opts.delete(:leader)
  @config_file = conf
  logger.info "Initializing Blessing::Runner for #{conf}"
  @opts = DEFAULT_OPTS.merge opts
  parse_configuration
end

Instance Attribute Details

#config_fileObject (readonly)

Returns the value of attribute config_file.



10
11
12
# File 'lib/blessing/runner.rb', line 10

def config_file
  @config_file
end

#optsObject (readonly)

Returns the value of attribute opts.



10
11
12
# File 'lib/blessing/runner.rb', line 10

def opts
  @opts
end

Instance Method Details

#check_reload(resurrect = false) ⇒ Object

Reload Unicorn if needed Ensure it did start up (This is the main cycle of Leader control)



90
91
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
# File 'lib/blessing/runner.rb', line 90

def check_reload(resurrect=false)
  logger.debug "Verifying #{@config_file.inspect}"

  # See if this Unicorn is alread dead
  if dead?
    unless resurrect
      logger.warn "Won't touch dead unicorn: PID=#{pid}, conf=#{@config_file.inspect}"
      return
    else 
      logger.warn "This unicorn is dead: PID=#{pid}, conf=#{@config_file.inspect}"
      logger.warn "Resurrecting..."
    end
  end

  # If configuration has changed, reload Unicorn
  if config_modified?
    reload
  end

  # In any case ensure it is running
  ensure_running

  # if it was dead and is now running, we are successful
  if dead?
    if running?
      logger.warn "Successfully resurrected (necromancy +1)!"
      @dead = false
    else
      logger.warn "Resurrection failed!"
    end
  end
end

#config_modification_timeObject

Gets the modification time of the configuration file



40
41
42
# File 'lib/blessing/runner.rb', line 40

def config_modification_time
  File.stat(@config_file).ctime
end

#config_modified?Boolean

Detect if the configuration has been modified

Returns:

  • (Boolean)


58
59
60
# File 'lib/blessing/runner.rb', line 58

def config_modified?
  @config_timestamp != config_modification_time
end

#dead?Boolean

is it totally dead?

Returns:

  • (Boolean)


147
148
149
# File 'lib/blessing/runner.rb', line 147

def dead?
  @dead
end

#ensure_runningObject

Ensure the Unicorn! process is running restarting it if needed



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/blessing/runner.rb', line 153

def ensure_running
  unless success = running?
    logger.info "Process is not running: pid=#{pid} conf=#{@config_file.inspect}"
    opts[:max_restarts].times do |i|
      logger.info "Restarting: try=#{i+1}"
      start
      sleep opts[:retry_delay]
      break if success = running?
    end
    if success
      logger.info "Successfully restarted"
      # just in case it was dead before
      @dead = false
    else
      logger.warn "Failed to restart in #{opts[:max_restarts]} tries, conf=#{@config_file.inspect}"
      @dead = true
    end
  end
  success
end

#loggerObject

Tap on leader logger facility or create one



27
28
29
30
31
32
33
34
35
36
37
# File 'lib/blessing/runner.rb', line 27

def logger
  if @leader
    @leader.logger
  else
    # We don't have leader connected, wont clutter stdout with log
    unless @logger
      @logger = Logger.new '/dev/null'
    end
    @logger
  end
end

#parse_configurationObject

Let Unicorn parse it’s config file



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/blessing/runner.rb', line 45

def parse_configuration
  @config_timestamp = config_modification_time
  conf_str = File.read @config_file

  # Parse parameters we are interested in
  [:pid].each do |key|
    if conf_str =~/#{key} (.*)/
      @opts[key] = eval $1
    end
  end
end

#reloadObject

Reload Unicorn! master process



124
125
126
127
128
129
130
131
# File 'lib/blessing/runner.rb', line 124

def reload
  logger.info "Reloading #{@config_file.inspect}"
  begin
    Process.kill "HUP", pid
  rescue => e
    # TODO: log unexpected error
  end
end

#running?Boolean

Verify the Unicorn! process is running

Returns:

  • (Boolean)


134
135
136
137
138
139
140
141
142
143
144
# File 'lib/blessing/runner.rb', line 134

def running?
  p = pid
  return false if p.nil?
  begin
    Process.kill 0, p
    true
  rescue Errno::ESRCH
    # just verifying; logging should be done elsewhere
    false
  end
end

#startObject

Starts the actual Unicorn! process



63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/blessing/runner.rb', line 63

def start
  logger.info "Starting Unicorn! process for #{@config_file.inspect}"
  fork do
    # Options to Unicorn! process
    options ={:config_file => @config_file}

    app = Unicorn.builder('config.ru',{})
    Unicorn::Launcher.daemonize!(options)
    Unicorn::HttpServer.new(app,options).start.join
  end
  Process.wait
end

#stopObject

Stops the actual Unicorn! process



77
78
79
80
81
82
83
84
85
# File 'lib/blessing/runner.rb', line 77

def stop
  logger.info "Stopping Unicorn! process for #{@config_file.inspect}"
  # Lets not panic if the process is already dead
  begin
    Process.kill "QUIT", pid
  rescue => e
    logger.warn "Process does not exist! PID=#{pid}, conf=#{@config_file.inspect}"
  end
end