bluepill 0.0.62 → 0.0.63

Sign up to get free protection for your applications and to get access to all the features.
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