dylanvaughn-bluepill 0.0.39

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,60 @@
1
+ module Bluepill
2
+ class Trigger
3
+ @implementations = {}
4
+ def self.inherited(klass)
5
+ @implementations[klass.name.split('::').last.underscore.to_sym] = klass
6
+ end
7
+
8
+ def self.[](name)
9
+ @implementations[name]
10
+ end
11
+
12
+ attr_accessor :process, :logger, :mutex, :scheduled_events
13
+
14
+ def initialize(process, options = {})
15
+ self.process = process
16
+ self.logger = options[:logger]
17
+ self.mutex = Mutex.new
18
+ self.scheduled_events = []
19
+ end
20
+
21
+ def reset!
22
+ self.cancel_all_events
23
+ end
24
+
25
+ def notify(transition)
26
+ raise "Implement in subclass"
27
+ end
28
+
29
+ def dispatch!(event)
30
+ self.process.dispatch!(event, self.class.name.split("::").last)
31
+ end
32
+
33
+ def schedule_event(event, delay)
34
+ # TODO: maybe wrap this in a ScheduledEvent class with methods like cancel
35
+ thread = Thread.new(self) do |trigger|
36
+ begin
37
+ sleep delay.to_f
38
+ trigger.logger.info("Retrying from flapping")
39
+ trigger.dispatch!(event)
40
+ trigger.mutex.synchronize do
41
+ trigger.scheduled_events.delete_if { |_, thread| thread == Thread.current }
42
+ end
43
+ rescue StandardError => e
44
+ trigger.logger.err(e)
45
+ trigger.logger.err(e.backtrace.join("\n"))
46
+ end
47
+ end
48
+
49
+ self.scheduled_events.push([event, thread])
50
+ end
51
+
52
+ def cancel_all_events
53
+ self.logger.info "Canceling all scheduled events"
54
+ self.mutex.synchronize do
55
+ self.scheduled_events.each {|_, thread| thread.kill}
56
+ end
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,59 @@
1
+ module Bluepill
2
+ module Triggers
3
+ class Flapping < Bluepill::Trigger
4
+ TRIGGER_STATES = [:starting, :restarting]
5
+
6
+ PARAMS = [:times, :within, :retry_in]
7
+
8
+ attr_accessor *PARAMS
9
+ attr_reader :timeline
10
+
11
+ def initialize(process, options = {})
12
+ options.reverse_merge!(:times => 5, :within => 1, :retry_in => 5)
13
+
14
+ options.each_pair do |name, val|
15
+ instance_variable_set("@#{name}", val) if PARAMS.include?(name)
16
+ end
17
+
18
+ @timeline = Util::RotationalArray.new(@times)
19
+ super
20
+ end
21
+
22
+ def notify(transition)
23
+ if TRIGGER_STATES.include?(transition.to_name)
24
+ self.timeline << Time.now.to_i
25
+ self.check_flapping
26
+ end
27
+ end
28
+
29
+ def reset!
30
+ @timeline.clear
31
+ super
32
+ end
33
+
34
+ def check_flapping
35
+ num_occurances = (@timeline.nitems == self.times)
36
+
37
+ # The process has not flapped if we haven't encountered enough incidents
38
+ return unless num_occurances
39
+
40
+ # Check if the incident happend within the timeframe
41
+ duration = (@timeline.last - @timeline.first) <= self.within
42
+
43
+ if duration
44
+ self.logger.info "Flapping detected: retrying in #{self.retry_in} seconds"
45
+
46
+ self.schedule_event(:start, self.retry_in)
47
+
48
+ # this happens in the process' thread so we don't have to worry about concurrency issues with this event
49
+ self.dispatch!(:unmonitor)
50
+
51
+ @timeline.clear
52
+
53
+ # This will prevent a transition from happening in the process state_machine
54
+ throw :halt
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,66 @@
1
+ module Bluepill
2
+ module Util
3
+ class RotationalArray < Array
4
+ def initialize(size)
5
+ super(size)
6
+
7
+ @capacity = size
8
+ @counter = 0
9
+ end
10
+
11
+ def push(value)
12
+ idx = rotational_idx(@counter)
13
+ self[idx] = value
14
+
15
+ @counter += 1
16
+ self
17
+ end
18
+
19
+ alias_method :<<, :push
20
+
21
+ def pop
22
+ raise "Cannot call pop on a rotational array"
23
+ end
24
+
25
+ def shift
26
+ raise "Cannot call shift on a rotational array"
27
+ end
28
+
29
+ def unshift
30
+ raise "Cannot call unshift on a rotational array"
31
+ end
32
+
33
+ def last
34
+ return if @counter.zero?
35
+
36
+ self[rotational_idx(@counter - 1)]
37
+ end
38
+
39
+ def first
40
+ return if @counter.zero?
41
+ return self[0] if @counter <= @capacity
42
+
43
+ self[rotational_idx(@counter)]
44
+ end
45
+
46
+ def clear
47
+ @counter = 0
48
+ super
49
+ end
50
+
51
+ def each(&block)
52
+ times = @counter >= @capacity ? @capacity : @counter
53
+ start = @counter >= @capacity ? rotational_idx(@counter) : 0
54
+ times.times do |i|
55
+ block.call(self[rotational_idx(start + i)])
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def rotational_idx(idx)
62
+ idx % @capacity
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,3 @@
1
+ module Bluepill
2
+ VERSION = "0.0.38"
3
+ end
data/lib/example.rb ADDED
@@ -0,0 +1,81 @@
1
+ require 'rubygems'
2
+ require 'bluepill'
3
+ require 'logger'
4
+
5
+ ROOT_DIR = "/tmp/bp"
6
+
7
+ # Watch with
8
+ # watch -n0.2 'ps axu | egrep "(CPU|forking|bluepill|sleep)" | grep -v grep | sort'
9
+ Bluepill.application(:sample_app) do |app|
10
+ 0.times do |i|
11
+ app.process("process_#{i}") do |process|
12
+ process.pid_file = "#{ROOT_DIR}/pids/process_#{i}.pid"
13
+
14
+ # I could not figure out a portable way to
15
+ # specify the path to the sample forking server across the diff developer laptops.
16
+ # Since this code is eval'ed we cannot reliably use __FILE__
17
+ process.start_command = "/Users/rohith/work/bluepill/bin/sample_forking_server #{4242 + i}"
18
+ process.stop_command = "kill -INT {{PID}}"
19
+ process.daemonize = true
20
+
21
+ process.start_grace_time = 1.seconds
22
+ process.restart_grace_time = 7.seconds
23
+ process.stop_grace_time = 7.seconds
24
+
25
+ process.uid = "rohith"
26
+ process.gid = "staff"
27
+
28
+ # process.checks :cpu_usage, :every => 10, :below => 0.5, :times => [5, 5]
29
+ process.checks :flapping, :times => 2, :within => 30.seconds, :retry_in => 7.seconds
30
+
31
+ process.monitor_children do |child_process|
32
+ # child_process.checks :cpu_usage,
33
+ # :every => 10,
34
+ # :below => 0.5,
35
+ # :times => [5, 5]
36
+
37
+ # child_process.checks :mem_usage,
38
+ # :every => 3,
39
+ # :below => 600.kilobytes,
40
+ # :times => [3, 5],
41
+ # :fires => [:stop]
42
+
43
+ child_process.stop_command = "kill -QUIT {{PID}}"
44
+ # child_process.checks :flapping, :times => 2, :within => 30.seconds, :retry_in => 7.seconds
45
+ end
46
+ end
47
+ end
48
+
49
+ 0.times do |i|
50
+ app.process("group_process_#{i}") do |process|
51
+ process.group = "group_1"
52
+ process.pid_file = "/Users/rohith/ffs/tmp/pids/mongrel_#{i}.pid"
53
+ process.start_command = "cd ~/ffs && mongrel_rails start -P #{process.pid_file} -p 3000 -d"
54
+
55
+ process.start_grace_time = 10.seconds
56
+
57
+ process.uid = "rohith"
58
+ process.gid = "staff"
59
+
60
+ # process.checks :always_true, :every => 10
61
+ end
62
+ end
63
+
64
+ 1.times do |i|
65
+ app.process("group_process_#{i}") do |process|
66
+ process.uid = "rohith"
67
+ process.gid = "wheel"
68
+
69
+ process.stderr = "/tmp/err.log"
70
+ process.stdout = "/tmp/err.log"
71
+
72
+
73
+ process.group = "grouped"
74
+ process.start_command = %Q{cd /tmp && ruby -e '$stderr.puts("hello stderr");$stdout.puts("hello stdout"); $stdout.flush; $stderr.flush; sleep 10'}
75
+ process.daemonize = true
76
+ process.pid_file = "/tmp/noperm/p_#{process.group}_#{i}.pid"
77
+
78
+ # process.checks :always_true, :every => 5
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require 'bluepill'
3
+ require 'logger'
4
+
5
+ # ATTENTION:
6
+ # You must declare only one application per config when foreground mode specified
7
+ #
8
+ # http://github.com/akzhan/runit-man used as example of monitored application.
9
+
10
+ Bluepill.application(:runit_man, :foreground => true) do |app|
11
+ app.process("runit-man") do |process|
12
+ process.pid_file = "/etc/service/runit-man/supervise/pid"
13
+
14
+ process.start_command = "/usr/bin/sv start runit-man"
15
+ process.stop_command = "/usr/bin/sv stop runit-man"
16
+ process.restart_command = "/usr/bin/sv restart runit-man"
17
+
18
+ process.start_grace_time = 1.seconds
19
+ process.restart_grace_time = 7.seconds
20
+ process.stop_grace_time = 7.seconds
21
+
22
+ process.checks :http, :within => 30.seconds, :retry_in => 7.seconds, :every => 30.seconds,
23
+ :url => 'http://localhost:4567/', :kind => :success, :pattern => /html/, :timeout => 3.seconds
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,167 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dylanvaughn-bluepill
3
+ version: !ruby/object:Gem::Version
4
+ hash: 81
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 39
10
+ version: 0.0.39
11
+ platform: ruby
12
+ authors:
13
+ - Arya Asemanfar
14
+ - Gary Tsang
15
+ - Rohith Ravi
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2010-08-06 00:00:00 -07:00
21
+ default_executable: bluepill
22
+ dependencies:
23
+ - !ruby/object:Gem::Dependency
24
+ name: daemons
25
+ prerelease: false
26
+ requirement: &id001 !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ hash: 5
32
+ segments:
33
+ - 1
34
+ - 0
35
+ - 9
36
+ version: 1.0.9
37
+ type: :runtime
38
+ version_requirements: *id001
39
+ - !ruby/object:Gem::Dependency
40
+ name: blankslate
41
+ prerelease: false
42
+ requirement: &id002 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ hash: 107
48
+ segments:
49
+ - 2
50
+ - 1
51
+ - 2
52
+ - 2
53
+ version: 2.1.2.2
54
+ type: :runtime
55
+ version_requirements: *id002
56
+ - !ruby/object:Gem::Dependency
57
+ name: state_machine
58
+ prerelease: false
59
+ requirement: &id003 !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ hash: 63
65
+ segments:
66
+ - 0
67
+ - 8
68
+ - 0
69
+ version: 0.8.0
70
+ type: :runtime
71
+ version_requirements: *id003
72
+ - !ruby/object:Gem::Dependency
73
+ name: activesupport
74
+ prerelease: false
75
+ requirement: &id004 !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ hash: 11
81
+ segments:
82
+ - 2
83
+ - 3
84
+ - 4
85
+ version: 2.3.4
86
+ type: :runtime
87
+ version_requirements: *id004
88
+ description: Bluepill keeps your daemons up while taking up as little resources as possible. After all you probably want the resources of your server to be used by whatever daemons you are running rather than the thing that's supposed to make sure they are brought back up, should they die or misbehave.
89
+ email: dylancvaughn@gmail.com
90
+ executables:
91
+ - bluepill
92
+ extensions: []
93
+
94
+ extra_rdoc_files:
95
+ - LICENSE
96
+ - README.md
97
+ files:
98
+ - .gitignore
99
+ - DESIGN.md
100
+ - LICENSE
101
+ - README.md
102
+ - Rakefile
103
+ - VERSION
104
+ - bin/bluepill
105
+ - bin/bpsv
106
+ - bluepill.gemspec
107
+ - lib/bluepill.rb
108
+ - lib/bluepill/application.rb
109
+ - lib/bluepill/application/client.rb
110
+ - lib/bluepill/application/server.rb
111
+ - lib/bluepill/condition_watch.rb
112
+ - lib/bluepill/controller.rb
113
+ - lib/bluepill/dsl.rb
114
+ - lib/bluepill/group.rb
115
+ - lib/bluepill/logger.rb
116
+ - lib/bluepill/process.rb
117
+ - lib/bluepill/process_conditions.rb
118
+ - lib/bluepill/process_conditions/always_true.rb
119
+ - lib/bluepill/process_conditions/cpu_usage.rb
120
+ - lib/bluepill/process_conditions/http.rb
121
+ - lib/bluepill/process_conditions/mem_usage.rb
122
+ - lib/bluepill/process_conditions/process_condition.rb
123
+ - lib/bluepill/process_statistics.rb
124
+ - lib/bluepill/socket.rb
125
+ - lib/bluepill/system.rb
126
+ - lib/bluepill/trigger.rb
127
+ - lib/bluepill/triggers/flapping.rb
128
+ - lib/bluepill/util/rotational_array.rb
129
+ - lib/bluepill/version.rb
130
+ - lib/example.rb
131
+ - lib/runit_example.rb
132
+ has_rdoc: true
133
+ homepage: http://github.com/arya/bluepill
134
+ licenses: []
135
+
136
+ post_install_message:
137
+ rdoc_options:
138
+ - --charset=UTF-8
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ hash: 3
147
+ segments:
148
+ - 0
149
+ version: "0"
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ none: false
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ hash: 3
156
+ segments:
157
+ - 0
158
+ version: "0"
159
+ requirements: []
160
+
161
+ rubyforge_project:
162
+ rubygems_version: 1.3.7
163
+ signing_key:
164
+ specification_version: 3
165
+ summary: A process monitor written in Ruby with stability and minimalism in mind.
166
+ test_files: []
167
+