bluepill 0.0.68 → 0.0.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
-