bluepill 0.0.68 → 0.0.69

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -2
  3. data/bin/bluepill +19 -20
  4. data/bin/sample_forking_server +26 -13
  5. data/bluepill.gemspec +12 -18
  6. data/lib/bluepill.rb +12 -12
  7. data/lib/bluepill/application.rb +64 -71
  8. data/lib/bluepill/application/client.rb +0 -2
  9. data/lib/bluepill/application/server.rb +1 -3
  10. data/lib/bluepill/condition_watch.rb +12 -7
  11. data/lib/bluepill/controller.rb +37 -42
  12. data/lib/bluepill/dsl.rb +1 -2
  13. data/lib/bluepill/dsl/app_proxy.rb +3 -4
  14. data/lib/bluepill/dsl/process_factory.rb +40 -44
  15. data/lib/bluepill/dsl/process_proxy.rb +4 -5
  16. data/lib/bluepill/group.rb +15 -21
  17. data/lib/bluepill/logger.rb +4 -4
  18. data/lib/bluepill/process.rb +107 -109
  19. data/lib/bluepill/process_conditions.rb +1 -3
  20. data/lib/bluepill/process_conditions/always_true.rb +2 -3
  21. data/lib/bluepill/process_conditions/cpu_usage.rb +0 -1
  22. data/lib/bluepill/process_conditions/file_time.rb +5 -6
  23. data/lib/bluepill/process_conditions/http.rb +11 -9
  24. data/lib/bluepill/process_conditions/mem_usage.rb +6 -7
  25. data/lib/bluepill/process_conditions/process_condition.rb +4 -5
  26. data/lib/bluepill/process_conditions/running_time.rb +1 -2
  27. data/lib/bluepill/process_conditions/zombie_process.rb +1 -2
  28. data/lib/bluepill/process_journal.rb +18 -21
  29. data/lib/bluepill/process_statistics.rb +2 -4
  30. data/lib/bluepill/socket.rb +13 -16
  31. data/lib/bluepill/system.rb +57 -63
  32. data/lib/bluepill/trigger.rb +9 -11
  33. data/lib/bluepill/triggers/flapping.rb +12 -16
  34. data/lib/bluepill/util/rotational_array.rb +1 -2
  35. data/lib/bluepill/version.rb +1 -2
  36. metadata +4 -28
  37. data/.gitignore +0 -12
  38. data/.rspec +0 -1
  39. data/.travis.yml +0 -17
  40. data/Gemfile +0 -27
  41. data/Rakefile +0 -38
  42. data/examples/example.rb +0 -87
  43. data/examples/new_example.rb +0 -89
  44. data/examples/new_runit_example.rb +0 -29
  45. data/examples/runit_example.rb +0 -26
  46. data/local-bluepill +0 -130
  47. data/spec/lib/bluepill/application_spec.rb +0 -51
  48. data/spec/lib/bluepill/logger_spec.rb +0 -3
  49. data/spec/lib/bluepill/process_spec.rb +0 -135
  50. data/spec/lib/bluepill/process_statistics_spec.rb +0 -24
  51. data/spec/lib/bluepill/system_spec.rb +0 -45
  52. data/spec/spec_helper.rb +0 -26
@@ -1,18 +1,17 @@
1
- # -*- encoding: utf-8 -*-
2
1
  module Bluepill
3
2
  class Logger
4
3
  LOG_METHODS = [:emerg, :alert, :crit, :err, :warning, :notice, :info, :debug]
5
4
 
6
5
  def initialize(options = {})
7
6
  @options = options
8
- @logger = options[:logger] || self.create_logger
7
+ @logger = options[:logger] || create_logger
9
8
  @prefix = options[:prefix]
10
9
  @stdout = options[:stdout]
11
10
  @prefixes = {}
12
11
  end
13
12
 
14
13
  LOG_METHODS.each do |method|
15
- eval <<-END
14
+ class_eval <<-END
16
15
  def #{method}(msg, prefix = [])
17
16
  if @logger.is_a?(self.class)
18
17
  @logger.#{method}(msg, [@prefix] + prefix)
@@ -40,7 +39,8 @@ module Bluepill
40
39
  end
41
40
  end
42
41
 
43
- protected
42
+ protected
43
+
44
44
  def create_logger
45
45
  if @options[:log_file]
46
46
  LoggerAdapter.new(@options[:log_file])
@@ -1,12 +1,10 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
1
  # fixes problem with loading on systems with rubyist-aasm installed
4
- gem "state_machine"
2
+ gem 'state_machine'
5
3
 
6
- require "state_machine"
7
- require "daemons"
8
- require "bluepill/system"
9
- require "bluepill/process_journal"
4
+ require 'state_machine'
5
+ require 'daemons'
6
+ require 'bluepill/system'
7
+ require 'bluepill/process_journal'
10
8
 
11
9
  module Bluepill
12
10
  class Process
@@ -49,12 +47,12 @@ module Bluepill
49
47
  :group_start_noblock,
50
48
  :group_restart_noblock,
51
49
  :group_stop_noblock,
52
- :group_unmonitor_noblock
50
+ :group_unmonitor_noblock,
53
51
 
54
52
  ]
55
53
 
56
54
  attr_accessor :name, :watches, :triggers, :logger, :skip_ticks_until, :process_running
57
- attr_accessor *CONFIGURABLE_ATTRIBUTES
55
+ attr_accessor(*CONFIGURABLE_ATTRIBUTES)
58
56
  attr_reader :children, :statistics
59
57
 
60
58
  state_machine :initial => :unmonitored do
@@ -124,9 +122,9 @@ module Bluepill
124
122
 
125
123
  checks.each do |name, opts|
126
124
  if Trigger[name]
127
- self.add_trigger(name, opts)
125
+ add_trigger(name, opts)
128
126
  else
129
- self.add_watch(name, opts)
127
+ add_watch(name, opts)
130
128
  end
131
129
  end
132
130
 
@@ -135,11 +133,11 @@ module Bluepill
135
133
  @cache_actual_pid = true
136
134
  @start_grace_time = @stop_grace_time = @restart_grace_time = 3
137
135
  @environment = {}
138
- @on_start_timeout = "start"
136
+ @on_start_timeout = 'start'
139
137
  @group_start_noblock = @group_stop_noblock = @group_restart_noblock = @group_unmonitor_noblock = true
140
138
 
141
139
  CONFIGURABLE_ATTRIBUTES.each do |attribute_name|
142
- self.send("#{attribute_name}=", options[attribute_name]) if options.has_key?(attribute_name)
140
+ send("#{attribute_name}=", options[attribute_name]) if options.key?(attribute_name)
143
141
  end
144
142
 
145
143
  # Let state_machine do its initialization stuff
@@ -159,64 +157,68 @@ module Bluepill
159
157
  # run state machine transitions
160
158
  super
161
159
 
162
- if self.up?
163
- self.run_watches
160
+ return unless self.up?
161
+ run_watches
164
162
 
165
- if self.monitor_children?
166
- refresh_children!
167
- children.each {|child| child.tick}
168
- end
169
- end
163
+ return unless self.monitor_children?
164
+ refresh_children!
165
+ children.each(&:tick)
170
166
  end
171
167
 
172
168
  def logger=(logger)
173
169
  @logger = logger
174
- self.watches.each {|w| w.logger = logger }
175
- self.triggers.each {|t| t.logger = logger }
170
+ watches.each { |w| w.logger = logger }
171
+ triggers.each { |t| t.logger = logger }
176
172
  end
177
173
 
178
174
  # State machine methods
179
175
  def dispatch!(event, reason = nil)
180
176
  @event_mutex.synchronize do
181
177
  @statistics.record_event(event, reason)
182
- self.send("#{event}")
178
+ send("#{event}")
183
179
  end
184
180
  end
185
181
 
186
182
  def record_transition(transition)
187
- unless transition.loopback?
188
- @transitioned = true
183
+ return if transition.loopback?
184
+ @transitioned = true
189
185
 
190
- # When a process changes state, we should clear the memory of all the watches
191
- self.watches.each { |w| w.clear_history! }
186
+ # When a process changes state, we should clear the memory of all the watches
187
+ watches.each(&:clear_history!)
192
188
 
193
- # Also, when a process changes state, we should re-populate its child list
194
- if self.monitor_children?
195
- self.logger.warning "Clearing child list"
196
- self.children.clear
197
- end
198
- logger.info "Going from #{transition.from_name} => #{transition.to_name}"
189
+ # Also, when a process changes state, we should re-populate its child list
190
+ if self.monitor_children?
191
+ logger.warning 'Clearing child list'
192
+ children.clear
199
193
  end
194
+ logger.info "Going from #{transition.from_name} => #{transition.to_name}"
200
195
  end
201
196
 
202
197
  def notify_triggers(transition)
203
- self.triggers.each {|trigger| trigger.notify(transition)}
198
+ triggers.each do |trigger|
199
+ begin
200
+ trigger.notify(transition)
201
+ rescue => e
202
+ logger.err e.backtrace
203
+ raise e
204
+ end
205
+ end
204
206
  end
205
207
 
206
208
  # Watch related methods
207
209
  def add_watch(name, options = {})
208
- self.watches << ConditionWatch.new(name, options.merge(:logger => self.logger))
210
+ watches << ConditionWatch.new(name, options.merge(:logger => logger))
209
211
  end
210
212
 
211
213
  def add_trigger(name, options = {})
212
- self.triggers << Trigger[name].new(self, options.merge(:logger => self.logger))
214
+ triggers << Trigger[name].new(self, options.merge(:logger => logger))
213
215
  end
214
216
 
215
217
  def run_watches
216
218
  now = Time.now.to_i
217
219
 
218
- threads = self.watches.collect do |watch|
219
- [watch, Thread.new { Thread.current[:events] = watch.run(self.actual_pid, now) }]
220
+ threads = watches.collect do |watch|
221
+ [watch, Thread.new { Thread.current[:events] = watch.run(actual_pid, now) }]
220
222
  end
221
223
 
222
224
  @transitioned = false
@@ -246,22 +248,22 @@ module Bluepill
246
248
 
247
249
  def handle_user_command(cmd)
248
250
  case cmd
249
- when "start"
251
+ when 'start'
250
252
  if self.process_running?(true)
251
- logger.warning("Refusing to re-run start command on an already running process.")
253
+ logger.warning('Refusing to re-run start command on an already running process.')
252
254
  else
253
- dispatch!(:start, "user initiated")
255
+ dispatch!(:start, 'user initiated')
254
256
  end
255
- when "stop"
257
+ when 'stop'
256
258
  stop_process
257
- dispatch!(:unmonitor, "user initiated")
258
- when "restart"
259
+ dispatch!(:unmonitor, 'user initiated')
260
+ when 'restart'
259
261
  restart_process
260
- when "unmonitor"
262
+ when 'unmonitor'
261
263
  # When the user issues an unmonitor cmd, reset any triggers so that
262
264
  # scheduled events gets cleared
263
- triggers.each {|t| t.reset! }
264
- dispatch!(:unmonitor, "user initiated")
265
+ triggers.each(&:reset!)
266
+ dispatch!(:unmonitor, 'user initiated')
265
267
  end
266
268
  end
267
269
 
@@ -271,7 +273,7 @@ module Bluepill
271
273
 
272
274
  @process_running ||= signal_process(0)
273
275
  # the process isn't running, so we should clear the PID
274
- self.clear_pid unless @process_running
276
+ clear_pid unless @process_running
275
277
  @process_running
276
278
  end
277
279
 
@@ -280,55 +282,54 @@ module Bluepill
280
282
  pre_start_process
281
283
  logger.warning "Executing start command: #{start_command}"
282
284
  if self.daemonize?
283
- daemon_id = System.daemonize(start_command, self.system_command_options)
285
+ daemon_id = System.daemonize(start_command, system_command_options)
284
286
  if daemon_id
285
287
  ProcessJournal.append_pid_to_journal(name, daemon_id)
286
- children.each {|child|
288
+ children.each do|child|
287
289
  ProcessJournal.append_pid_to_journal(name, child.actual_id)
288
- } if self.monitor_children?
290
+ end if self.monitor_children?
289
291
  end
290
292
  daemon_id
291
293
  else
292
294
  # This is a self-daemonizing process
293
295
  with_timeout(start_grace_time, on_start_timeout) do
294
- result = System.execute_blocking(start_command, self.system_command_options)
296
+ result = System.execute_blocking(start_command, system_command_options)
295
297
 
296
298
  unless result[:exit_code].zero?
297
- logger.warning "Start command execution returned non-zero exit code:"
299
+ logger.warning 'Start command execution returned non-zero exit code:'
298
300
  logger.warning result.inspect
299
301
  end
300
302
  end
301
303
  end
302
304
 
303
- self.skip_ticks_for(start_grace_time)
305
+ skip_ticks_for(start_grace_time)
304
306
  end
305
307
 
306
308
  def pre_start_process
307
309
  return unless pre_start_command
308
310
  logger.warning "Executing pre start command: #{pre_start_command}"
309
- result = System.execute_blocking(pre_start_command, self.system_command_options)
310
- unless result[:exit_code].zero?
311
- logger.warning "Pre start command execution returned non-zero exit code:"
312
- logger.warning result.inspect
313
- end
311
+ result = System.execute_blocking(pre_start_command, system_command_options)
312
+ return if result[:exit_code].zero?
313
+ logger.warning 'Pre start command execution returned non-zero exit code:'
314
+ logger.warning result.inspect
314
315
  end
315
316
 
316
317
  def stop_process
317
318
  if monitor_children
318
- System.get_children(self.actual_pid).each do |child_pid|
319
+ System.get_children(actual_pid).each do |child_pid|
319
320
  ProcessJournal.append_pid_to_journal(name, child_pid)
320
321
  end
321
322
  end
322
323
 
323
324
  if stop_command
324
- cmd = self.prepare_command(stop_command)
325
+ cmd = prepare_command(stop_command)
325
326
  logger.warning "Executing stop command: #{cmd}"
326
327
 
327
- with_timeout(stop_grace_time, "stop") do
328
- result = System.execute_blocking(cmd, self.system_command_options)
328
+ with_timeout(stop_grace_time, 'stop') do
329
+ result = System.execute_blocking(cmd, system_command_options)
329
330
 
330
331
  unless result[:exit_code].zero?
331
- logger.warning "Stop command execution returned non-zero exit code:"
332
+ logger.warning 'Stop command execution returned non-zero exit code:'
332
333
  logger.warning result.inspect
333
334
  end
334
335
  end
@@ -348,9 +349,9 @@ module Bluepill
348
349
 
349
350
  logger.debug "Sleeping for #{delay} seconds"
350
351
  sleep delay
351
- #break unless signal_process(0) #break unless the process can be reached
352
+ # break unless signal_process(0) #break unless the process can be reached
352
353
  unless process.signal_process(0)
353
- logger.debug "Process has terminated."
354
+ logger.debug 'Process has terminated.'
354
355
  break
355
356
  end
356
357
  logger.info "Sending signal #{signal} to #{process.actual_pid}"
@@ -359,55 +360,55 @@ module Bluepill
359
360
  end
360
361
  else
361
362
  logger.warning "Executing default stop command. Sending TERM signal to #{actual_pid}"
362
- signal_process("TERM")
363
+ signal_process('TERM')
363
364
  end
364
365
  ProcessJournal.kill_all_from_journal(name) # finish cleanup
365
- self.unlink_pid # TODO: we only write the pid file if we daemonize, should we only unlink it if we daemonize?
366
+ unlink_pid # TODO: we only write the pid file if we daemonize, should we only unlink it if we daemonize?
366
367
 
367
- self.skip_ticks_for(stop_grace_time)
368
+ skip_ticks_for(stop_grace_time)
368
369
  end
369
370
 
370
371
  def restart_process
371
372
  if restart_command
372
- cmd = self.prepare_command(restart_command)
373
+ cmd = prepare_command(restart_command)
373
374
 
374
375
  logger.warning "Executing restart command: #{cmd}"
375
376
 
376
- with_timeout(restart_grace_time, "restart") do
377
- result = System.execute_blocking(cmd, self.system_command_options)
377
+ with_timeout(restart_grace_time, 'restart') do
378
+ result = System.execute_blocking(cmd, system_command_options)
378
379
 
379
380
  unless result[:exit_code].zero?
380
- logger.warning "Restart command execution returned non-zero exit code:"
381
+ logger.warning 'Restart command execution returned non-zero exit code:'
381
382
  logger.warning result.inspect
382
383
  end
383
384
  end
384
385
 
385
- self.skip_ticks_for(restart_grace_time)
386
+ skip_ticks_for(restart_grace_time)
386
387
  else
387
- logger.warning "No restart_command specified. Must stop and start to restart"
388
- self.stop_process
389
- self.start_process
388
+ logger.warning 'No restart_command specified. Must stop and start to restart'
389
+ stop_process
390
+ start_process
390
391
  end
391
392
  end
392
393
 
393
394
  def clean_threads
394
- @threads.each { |t| t.kill }
395
+ @threads.each(&:kill)
395
396
  @threads.clear
396
397
  end
397
398
 
398
399
  def daemonize?
399
- !!self.daemonize
400
+ !!daemonize
400
401
  end
401
402
 
402
403
  def monitor_children?
403
- !!self.monitor_children
404
+ !!monitor_children
404
405
  end
405
406
 
406
407
  def signal_process(code)
407
408
  code = code.to_s.upcase if code.is_a?(String) || code.is_a?(Symbol)
408
409
  ::Process.kill(code, actual_pid)
409
410
  true
410
- rescue Exception => e
411
+ rescue => e
411
412
  logger.err "Failed to signal process #{actual_pid} with code #{code}: #{e}"
412
413
  false
413
414
  end
@@ -424,7 +425,7 @@ module Bluepill
424
425
  return @actual_pid if cache_actual_pid? && @actual_pid
425
426
  @actual_pid = begin
426
427
  if pid_file
427
- if File.exists?(pid_file)
428
+ if File.exist?(pid_file)
428
429
  str = File.read(pid_file)
429
430
  str.to_i if str.size > 0
430
431
  else
@@ -436,7 +437,7 @@ module Bluepill
436
437
  end
437
438
 
438
439
  def pid_from_command
439
- pid = %x{#{pid_command}}.strip
440
+ pid = `#{pid_command}`.strip
440
441
  (pid =~ /\A\d+\z/) ? pid.to_i : nil
441
442
  end
442
443
 
@@ -453,26 +454,26 @@ module Bluepill
453
454
  System.delete_if_exists(pid_file)
454
455
  end
455
456
 
456
- # Internal State Methods
457
+ # Internal State Methods
457
458
  def skip_ticks_for(seconds)
458
459
  # TODO: should this be addative or longest wins?
459
460
  # i.e. if two calls for skip_ticks_for come in for 5 and 10, should it skip for 10 or 15?
460
- self.skip_ticks_until = (self.skip_ticks_until || Time.now.to_i) + seconds.to_i
461
+ self.skip_ticks_until = (skip_ticks_until || Time.now.to_i) + seconds.to_i
461
462
  end
462
463
 
463
464
  def skipping_ticks?
464
- self.skip_ticks_until && self.skip_ticks_until > Time.now.to_i
465
+ skip_ticks_until && skip_ticks_until > Time.now.to_i
465
466
  end
466
467
 
467
468
  def refresh_children!
468
469
  # First prune the list of dead children
469
- @children.delete_if {|child| !child.process_running?(true) }
470
+ @children.delete_if { |child| !child.process_running?(true) }
470
471
 
471
472
  # Add new found children to the list
472
- new_children_pids = System.get_children(self.actual_pid) - @children.map {|child| child.actual_pid}
473
+ new_children_pids = System.get_children(actual_pid) - @children.map(&:actual_pid)
473
474
 
474
475
  unless new_children_pids.empty?
475
- logger.info "Existing children: #{@children.collect{|c| c.actual_pid}.join(",")}. Got new children: #{new_children_pids.inspect} for #{actual_pid}"
476
+ logger.info "Existing children: #{@children.collect(&:actual_pid).join(',')}. Got new children: #{new_children_pids.inspect} for #{actual_pid}"
476
477
  end
477
478
 
478
479
  # Construct a new process wrapper for each new found children
@@ -481,41 +482,38 @@ module Bluepill
481
482
  child_name = "<child(pid:#{child_pid})>"
482
483
  logger = self.logger.prefix_with(child_name)
483
484
 
484
- child = self.child_process_factory.create_child_process(child_name, child_pid, logger)
485
+ child = child_process_factory.create_child_process(child_name, child_pid, logger)
485
486
  @children << child
486
487
  end
487
488
  end
488
489
 
489
490
  def prepare_command(command)
490
- command.to_s.gsub("{{PID}}", actual_pid.to_s)
491
+ command.to_s.gsub('{{PID}}', actual_pid.to_s)
491
492
  end
492
493
 
493
494
  def system_command_options
494
495
  {
495
- :uid => self.uid,
496
- :gid => self.gid,
497
- :working_dir => self.working_dir,
498
- :environment => self.environment,
499
- :pid_file => self.pid_file,
500
- :logger => self.logger,
501
- :stdin => self.stdin,
502
- :stdout => self.stdout,
503
- :stderr => self.stderr,
504
- :supplementary_groups => self.supplementary_groups
496
+ :uid => uid,
497
+ :gid => gid,
498
+ :working_dir => working_dir,
499
+ :environment => environment,
500
+ :pid_file => pid_file,
501
+ :logger => logger,
502
+ :stdin => stdin,
503
+ :stdout => stdout,
504
+ :stderr => stderr,
505
+ :supplementary_groups => supplementary_groups,
505
506
  }
506
507
  end
507
508
 
508
509
  def with_timeout(secs, next_state = nil, &blk)
509
510
  # Attempt to execute the passed block. If the block takes
510
511
  # too long, transition to the indicated next state.
511
- begin
512
- Timeout.timeout(secs.to_f, &blk)
513
- rescue Timeout::Error
514
- logger.err "Execution is taking longer than expected."
515
- logger.err "Did you forget to tell bluepill to daemonize this process?"
516
- dispatch!(next_state)
517
- end
512
+ Timeout.timeout(secs.to_f, &blk)
513
+ rescue Timeout::Error
514
+ logger.err 'Execution is taking longer than expected.'
515
+ logger.err 'Did you forget to tell bluepill to daemonize this process?'
516
+ dispatch!(next_state)
518
517
  end
519
518
  end
520
519
  end
521
-