bluepill 0.0.62 → 0.0.63

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.
data/.gitignore CHANGED
@@ -8,3 +8,5 @@ pkg/
8
8
  .bundle/
9
9
  Gemfile.lock
10
10
  .idea
11
+ bluepill.log
12
+
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in bluepill.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -72,6 +72,23 @@ To watch memory usage, we just add one more line:
72
72
  end
73
73
  ```
74
74
 
75
+ To watch the modification time of a file, e.g. a log file to ensure the process is actually working add one more line:
76
+
77
+ ```ruby
78
+ Bluepill.application("app_name") do |app|
79
+ app.process("process_name") do |process|
80
+ process.start_command = "/usr/bin/some_start_command"
81
+ process.pid_file = "/tmp/some_pid_file.pid"
82
+
83
+ process.checks :cpu_usage, :every => 10.seconds, :below => 5, :times => 3
84
+ process.checks :mem_usage, :every => 10.seconds, :below => 100.megabytes, :times => [3,5]
85
+ process.checks :file_time, :every => 60.seconds, :below => 3.minutes, :filename => "/tmp/some_file.log", :times => 2
86
+ end
87
+ end
88
+ ```
89
+
90
+
91
+
75
92
  We can tell bluepill to give a process some grace time to start/stop/restart before resuming monitoring:
76
93
 
77
94
  ```ruby
@@ -21,9 +21,7 @@ Gem::Specification.new do |s|
21
21
 
22
22
  s.add_development_dependency 'bundler', '>= 1.0.10'
23
23
  s.add_development_dependency 'rake', '!= 0.9.0'
24
- s.add_development_dependency 'rspec-core', '~> 2.0'
25
- s.add_development_dependency 'rspec-expectations', '~> 2.0'
26
- s.add_development_dependency 'rr', '~> 1.0'
24
+ s.add_development_dependency 'rspec', '~> 2.12.0'
27
25
  s.add_development_dependency 'faker', '~> 0.9'
28
26
  s.add_development_dependency 'yard', '~> 0.7'
29
27
 
@@ -16,7 +16,7 @@ module Bluepill
16
16
 
17
17
  @foreground = options[:foreground]
18
18
  self.log_file = options[:log_file]
19
- self.base_dir = options[:base_dir] || '/var/run/bluepill'
19
+ self.base_dir = ProcessJournal.base_dir = options[:base_dir] || '/var/run/bluepill'
20
20
  self.pid_file = File.join(self.base_dir, 'pids', self.name + ".pid")
21
21
  self.pids_dir = File.join(self.base_dir, 'pids', self.name)
22
22
  self.kill_timeout = options[:kill_timeout] || 10
@@ -36,9 +36,17 @@ module Bluepill
36
36
  self.processes.each do |process|
37
37
  next if process_name && process_name != process.name
38
38
  affected << [self.name, process.name].join(":")
39
- threads << Thread.new { process.handle_user_command("#{event}") }
39
+ noblock = process.group_#{event}_noblock
40
+ if noblock
41
+ self.logger.debug("Command #{event} running in non-blocking mode.")
42
+ threads << Thread.new { process.handle_user_command("#{event}") }
43
+ else
44
+ self.logger.debug("Command #{event} running in blocking mode.")
45
+ thread = Thread.new { process.handle_user_command("#{event}") }
46
+ thread.join
47
+ end
40
48
  end
41
- threads.each { |t| t.join }
49
+ threads.each { |t| t.join } unless threads.nil?
42
50
  affected
43
51
  end
44
52
  END
@@ -69,4 +77,4 @@ module Bluepill
69
77
  end
70
78
 
71
79
  end
72
- end
80
+ end
@@ -42,7 +42,15 @@ module Bluepill
42
42
 
43
43
  :supplementary_groups,
44
44
 
45
- :stop_signals
45
+ :stop_signals,
46
+
47
+ :on_start_timeout,
48
+
49
+ :group_start_noblock,
50
+ :group_restart_noblock,
51
+ :group_stop_noblock,
52
+ :group_unmonitor_noblock
53
+
46
54
  ]
47
55
 
48
56
  attr_accessor :name, :watches, :triggers, :logger, :skip_ticks_until, :process_running
@@ -127,6 +135,8 @@ module Bluepill
127
135
  @cache_actual_pid = true
128
136
  @start_grace_time = @stop_grace_time = @restart_grace_time = 3
129
137
  @environment = {}
138
+ @on_start_timeout = "start"
139
+ @group_start_noblock = @group_stop_noblock = @group_restart_noblock = @group_unmonitor_noblock = true
130
140
 
131
141
  CONFIGURABLE_ATTRIBUTES.each do |attribute_name|
132
142
  self.send("#{attribute_name}=", options[attribute_name]) if options.has_key?(attribute_name)
@@ -274,14 +284,13 @@ module Bluepill
274
284
  if daemon_id > 0
275
285
  ProcessJournal.append_pid_to_journal(name, daemon_id)
276
286
  children.each {|child|
277
- child_pid = child.actual_id
278
- ProcessJournal.append_pid_to_journal(name, child_id)
287
+ ProcessJournal.append_pid_to_journal(name, child.actual_id)
279
288
  } if self.monitor_children?
280
289
  end
281
290
  daemon_id
282
291
  else
283
292
  # This is a self-daemonizing process
284
- with_timeout(start_grace_time) do
293
+ with_timeout(start_grace_time, on_start_timeout) do
285
294
  result = System.execute_blocking(start_command, self.system_command_options)
286
295
 
287
296
  unless result[:exit_code].zero?
@@ -315,7 +324,7 @@ module Bluepill
315
324
  cmd = self.prepare_command(stop_command)
316
325
  logger.warning "Executing stop command: #{cmd}"
317
326
 
318
- with_timeout(stop_grace_time) do
327
+ with_timeout(stop_grace_time, "stop") do
319
328
  result = System.execute_blocking(cmd, self.system_command_options)
320
329
 
321
330
  unless result[:exit_code].zero?
@@ -364,7 +373,7 @@ module Bluepill
364
373
 
365
374
  logger.warning "Executing restart command: #{cmd}"
366
375
 
367
- with_timeout(restart_grace_time) do
376
+ with_timeout(restart_grace_time, "restart") do
368
377
  result = System.execute_blocking(cmd, self.system_command_options)
369
378
 
370
379
  unless result[:exit_code].zero?
@@ -469,10 +478,10 @@ module Bluepill
469
478
  # Construct a new process wrapper for each new found children
470
479
  new_children_pids.each do |child_pid|
471
480
  ProcessJournal.append_pid_to_journal(name, child_pid)
472
- name = "<child(pid:#{child_pid})>"
473
- logger = self.logger.prefix_with(name)
481
+ child_name = "<child(pid:#{child_pid})>"
482
+ logger = self.logger.prefix_with(child_name)
474
483
 
475
- child = self.child_process_factory.create_child_process(name, child_pid, logger)
484
+ child = self.child_process_factory.create_child_process(child_name, child_pid, logger)
476
485
  @children << child
477
486
  end
478
487
  end
@@ -496,13 +505,16 @@ module Bluepill
496
505
  }
497
506
  end
498
507
 
499
- def with_timeout(secs, &blk)
500
- Timeout.timeout(secs.to_f, &blk)
501
-
502
- rescue Timeout::Error
503
- logger.err "Execution is taking longer than expected. Unmonitoring."
504
- logger.err "Did you forget to tell bluepill to daemonize this process?"
505
- self.dispatch!("unmonitor")
508
+ def with_timeout(secs, next_state = nil, &blk)
509
+ # Attempt to execute the passed block. If the block takes
510
+ # 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
506
518
  end
507
519
  end
508
520
  end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Bluepill
3
+ module ProcessConditions
4
+ class FileTime < ProcessCondition
5
+ def initialize(options = {})
6
+ @below = options[:below]
7
+ @filename = options[:filename]
8
+ end
9
+
10
+ def run(pid, include_children)
11
+ if File.exists?(@filename)
12
+ Time.now()-File::mtime(@filename)
13
+ else
14
+ nil
15
+ end
16
+ rescue
17
+ $!
18
+ end
19
+
20
+ def check(value)
21
+ return false if value.nil?
22
+ return value < @below
23
+ end
24
+ end
25
+ end
26
+ end
@@ -29,7 +29,7 @@ module Bluepill
29
29
  session.read_timeout = @read_timeout
30
30
  hide_net_http_bug do
31
31
  session.start do |http|
32
- http.get(@uri.path)
32
+ http.get(@uri.request_uri)
33
33
  end
34
34
  end
35
35
  rescue
@@ -6,10 +6,17 @@ module Bluepill
6
6
 
7
7
  class << self
8
8
  attr_reader :logger
9
+ attr_reader :journal_base_dir
9
10
 
10
11
  def logger=(new_logger)
11
12
  @logger ||= new_logger
12
13
  end
14
+
15
+ def base_dir=(base_dir)
16
+ @journal_base_dir ||= File.join(base_dir, "journals")
17
+ FileUtils.mkdir_p(@journal_base_dir) unless File.exists?(@journal_base_dir)
18
+ FileUtils.chmod(0777, @journal_base_dir)
19
+ end
13
20
  end
14
21
 
15
22
  def skip_pid?(pid)
@@ -49,15 +56,15 @@ module Bluepill
49
56
  end
50
57
 
51
58
  def pid_journal_filename(journal_name)
52
- ".bluepill_pids_journal.#{journal_name}"
59
+ File.join(@journal_base_dir, ".bluepill_pids_journal.#{journal_name}")
53
60
  end
54
61
 
55
62
  def pgid_journal_filename(journal_name)
56
- ".bluepill_pgids_journal.#{journal_name}"
63
+ File.join(@journal_base_dir, ".bluepill_pgids_journal.#{journal_name}")
57
64
  end
58
65
 
59
66
  def pid_journal(filename)
60
- logger.debug("pid journal PWD=#{Dir.pwd}")
67
+ logger.debug("pid journal file: #{filename}")
61
68
  result = File.open(filename, 'r').readlines.map(&:to_i).reject {|pid| skip_pid?(pid)}
62
69
  logger.debug("pid journal = #{result.join(' ')}")
63
70
  result
@@ -66,7 +73,7 @@ module Bluepill
66
73
  end
67
74
 
68
75
  def pgid_journal(filename)
69
- logger.debug("pgid journal PWD=#{Dir.pwd}")
76
+ logger.debug("pgid journal file: #{filename}")
70
77
  result = File.open(filename, 'r').readlines.map(&:to_i).reject {|pgid| skip_pgid?(pgid)}
71
78
  logger.debug("pgid journal = #{result.join(' ')}")
72
79
  result
@@ -106,11 +106,14 @@ module Bluepill
106
106
 
107
107
  def delete_if_exists(filename)
108
108
  tries = 0
109
- File.unlink(filename) if filename && File.exists?(filename)
110
- rescue IOError, Errno::ENOENT
111
- rescue Errno::EACCES
112
- retry if (tries += 1) < 3
113
- $stderr.puts("Warning: permission denied trying to delete #{filename}")
109
+
110
+ begin
111
+ File.unlink(filename) if filename && File.exists?(filename)
112
+ rescue IOError, Errno::ENOENT
113
+ rescue Errno::EACCES
114
+ retry if (tries += 1) < 3
115
+ $stderr.puts("Warning: permission denied trying to delete #{filename}")
116
+ end
114
117
  end
115
118
 
116
119
  # Returns the stdout, stderr and exit code of the cmd
@@ -1,4 +1,4 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  module Bluepill
3
- VERSION = "0.0.62".freeze
3
+ VERSION = "0.0.63".freeze
4
4
  end
@@ -0,0 +1,102 @@
1
+ describe Bluepill::Process do
2
+ before(:all) do
3
+ Bluepill::ProcessJournal.base_dir = './.bluepill'
4
+ Bluepill::ProcessJournal.logger = Bluepill::Logger.new(:log_file => 'bluepill.log', :stdout => false).prefix_with('rspec')
5
+ end
6
+
7
+ subject do
8
+ Bluepill::Process.new(:proc_name, [],
9
+ :logger => Bluepill::Logger.new,
10
+ )
11
+ end
12
+
13
+ describe "#initialize" do
14
+ context "defaults" do
15
+ [
16
+ :start_command, :stop_command, :restart_command, :stdout, :stderr, :stdin,
17
+ :daemonize, :pid_file, :working_dir, :uid, :gid, :child_process_factory,
18
+ :pid_command, :auto_start, :supplementary_groups, :stop_signals
19
+ ].each do |attr|
20
+ its(attr) { should be_nil }
21
+ end
22
+ its(:monitor_children) { should be_false }
23
+ its(:cache_actual_pid) { should be_true }
24
+ its(:start_grace_time) { should eq 3 }
25
+ its(:stop_grace_time) { should eq 3 }
26
+ its(:restart_grace_time) { should eq 3 }
27
+ its(:on_start_timeout) { should eq "start" }
28
+ its(:environment) { should eq Hash[] }
29
+ end
30
+
31
+ context "overrides" do
32
+ subject { Bluepill::Process.new(:proc_name, [], :start_grace_time => 17) }
33
+ its(:start_grace_time) { should eq 17 }
34
+ end
35
+ end
36
+
37
+ describe "#start_process" do
38
+ it "functions" do
39
+ subject.stub(:start_command) { "/etc/init.d/script start" }
40
+ subject.stub(:on_start_timeout) { "freakout" }
41
+ subject.logger.stub(:warning)
42
+ subject.stub(:daemonize?) { false }
43
+
44
+ subject.should_receive(:with_timeout)
45
+ .with(3, "freakout")
46
+ .and_yield
47
+
48
+ Bluepill::System.should_receive(:execute_blocking)
49
+ .with("/etc/init.d/script start", subject.system_command_options)
50
+ .and_return(exit_code: 0)
51
+
52
+ subject.start_process
53
+ end
54
+
55
+ describe "#stop_process" do
56
+ it "functions" do
57
+ subject.stub(:stop_command) { "/etc/init.d/script stop" }
58
+ subject.logger.stub(:warning)
59
+ subject.should_receive(:with_timeout)
60
+ .with(3, "stop")
61
+ .and_yield
62
+
63
+ Bluepill::System.should_receive(:execute_blocking)
64
+ .with("/etc/init.d/script stop", subject.system_command_options)
65
+ .and_return(exit_code: 0)
66
+
67
+ subject.stop_process
68
+ end
69
+ end
70
+
71
+ describe "#restart_process" do
72
+ it "functions" do
73
+ subject.stub(:restart_command) { "/etc/init.d/script restart" }
74
+ subject.logger.stub(:warning)
75
+ subject.should_receive(:with_timeout)
76
+ .with(3, "restart")
77
+ .and_yield
78
+
79
+ Bluepill::System.should_receive(:execute_blocking)
80
+ .with("/etc/init.d/script restart", subject.system_command_options)
81
+ .and_return(exit_code: 0)
82
+
83
+ subject.restart_process
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "#with_timeout" do
89
+ let(:block) { proc { nil } }
90
+
91
+ before(:each) do
92
+ subject.logger.stub(:err)
93
+ Timeout.should_receive(:timeout).with(3.to_f, &block).and_raise(Timeout::Error)
94
+ end
95
+
96
+ it "proceeds to next_state on timeout." do
97
+ subject.should_receive(:dispatch!).with("state_override")
98
+ subject.with_timeout(3, "state_override", &block)
99
+ end
100
+ end
101
+
102
+ end
@@ -1,12 +1,12 @@
1
1
  describe Bluepill::System do
2
2
  describe :pid_alive? do
3
3
  it "should be true if process responds to zero signal" do
4
- mock(::Process).kill(0, 555)
4
+ Process.should_receive(:kill).with(0, 555).and_return(0)
5
5
  Bluepill::System.should be_pid_alive(555)
6
6
  end
7
7
 
8
8
  it "should be false if process throws exception on zero signal" do
9
- mock(::Process).kill(0, 555) { raise Errno::ESRCH.new }
9
+ Process.should_receive(:kill).with(0, 555).and_raise(Errno::ESRCH)
10
10
  Bluepill::System.should_not be_pid_alive(555)
11
11
  end
12
12
  end
@@ -33,4 +33,4 @@ describe Bluepill::System do
33
33
  Bluepill::System.store.should be_empty
34
34
  end
35
35
  end
36
- end
36
+ end
@@ -12,8 +12,4 @@ require 'rspec/core'
12
12
 
13
13
  $LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
14
14
 
15
- RSpec.configure do |conf|
16
- conf.mock_with :rr
17
- end
18
-
19
15
  require 'bluepill'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bluepill
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.62
4
+ version: 0.0.63
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-03-19 00:00:00.000000000 Z
14
+ date: 2013-04-21 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: daemons
@@ -110,13 +110,13 @@ dependencies:
110
110
  - !ruby/object:Gem::Version
111
111
  version: 0.9.0
112
112
  - !ruby/object:Gem::Dependency
113
- name: rspec-core
113
+ name: rspec
114
114
  requirement: !ruby/object:Gem::Requirement
115
115
  none: false
116
116
  requirements:
117
117
  - - ~>
118
118
  - !ruby/object:Gem::Version
119
- version: '2.0'
119
+ version: 2.12.0
120
120
  type: :development
121
121
  prerelease: false
122
122
  version_requirements: !ruby/object:Gem::Requirement
@@ -124,39 +124,7 @@ dependencies:
124
124
  requirements:
125
125
  - - ~>
126
126
  - !ruby/object:Gem::Version
127
- version: '2.0'
128
- - !ruby/object:Gem::Dependency
129
- name: rspec-expectations
130
- requirement: !ruby/object:Gem::Requirement
131
- none: false
132
- requirements:
133
- - - ~>
134
- - !ruby/object:Gem::Version
135
- version: '2.0'
136
- type: :development
137
- prerelease: false
138
- version_requirements: !ruby/object:Gem::Requirement
139
- none: false
140
- requirements:
141
- - - ~>
142
- - !ruby/object:Gem::Version
143
- version: '2.0'
144
- - !ruby/object:Gem::Dependency
145
- name: rr
146
- requirement: !ruby/object:Gem::Requirement
147
- none: false
148
- requirements:
149
- - - ~>
150
- - !ruby/object:Gem::Version
151
- version: '1.0'
152
- type: :development
153
- prerelease: false
154
- version_requirements: !ruby/object:Gem::Requirement
155
- none: false
156
- requirements:
157
- - - ~>
158
- - !ruby/object:Gem::Version
159
- version: '1.0'
127
+ version: 2.12.0
160
128
  - !ruby/object:Gem::Dependency
161
129
  name: faker
162
130
  requirement: !ruby/object:Gem::Requirement
@@ -235,6 +203,7 @@ files:
235
203
  - lib/bluepill/process_conditions.rb
236
204
  - lib/bluepill/process_conditions/always_true.rb
237
205
  - lib/bluepill/process_conditions/cpu_usage.rb
206
+ - lib/bluepill/process_conditions/file_time.rb
238
207
  - lib/bluepill/process_conditions/http.rb
239
208
  - lib/bluepill/process_conditions/mem_usage.rb
240
209
  - lib/bluepill/process_conditions/process_condition.rb
@@ -248,6 +217,7 @@ files:
248
217
  - lib/bluepill/version.rb
249
218
  - local-bluepill
250
219
  - spec/lib/bluepill/logger_spec.rb
220
+ - spec/lib/bluepill/process_spec.rb
251
221
  - spec/lib/bluepill/process_statistics_spec.rb
252
222
  - spec/lib/bluepill/system_spec.rb
253
223
  - spec/spec_helper.rb
@@ -263,12 +233,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
263
233
  - - ! '>='
264
234
  - !ruby/object:Gem::Version
265
235
  version: '0'
236
+ segments:
237
+ - 0
238
+ hash: 4302574804058768595
266
239
  required_rubygems_version: !ruby/object:Gem::Requirement
267
240
  none: false
268
241
  requirements:
269
242
  - - ! '>='
270
243
  - !ruby/object:Gem::Version
271
244
  version: '0'
245
+ segments:
246
+ - 0
247
+ hash: 4302574804058768595
272
248
  requirements: []
273
249
  rubyforge_project:
274
250
  rubygems_version: 1.8.24
@@ -277,6 +253,7 @@ specification_version: 3
277
253
  summary: A process monitor written in Ruby with stability and minimalism in mind.
278
254
  test_files:
279
255
  - spec/lib/bluepill/logger_spec.rb
256
+ - spec/lib/bluepill/process_spec.rb
280
257
  - spec/lib/bluepill/process_statistics_spec.rb
281
258
  - spec/lib/bluepill/system_spec.rb
282
259
  - spec/spec_helper.rb