kostya-bluepill 0.0.60.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.gitignore +10 -0
  2. data/.rspec +1 -0
  3. data/DESIGN.md +10 -0
  4. data/Gemfile +10 -0
  5. data/LICENSE +22 -0
  6. data/README.md +305 -0
  7. data/Rakefile +38 -0
  8. data/bin/bluepill +104 -0
  9. data/bluepill.gemspec +37 -0
  10. data/examples/example.rb +87 -0
  11. data/examples/new_example.rb +89 -0
  12. data/examples/new_runit_example.rb +29 -0
  13. data/examples/runit_example.rb +26 -0
  14. data/lib/bluepill.rb +38 -0
  15. data/lib/bluepill/application.rb +201 -0
  16. data/lib/bluepill/application/client.rb +8 -0
  17. data/lib/bluepill/application/server.rb +23 -0
  18. data/lib/bluepill/condition_watch.rb +50 -0
  19. data/lib/bluepill/controller.rb +110 -0
  20. data/lib/bluepill/dsl.rb +12 -0
  21. data/lib/bluepill/dsl/app_proxy.rb +25 -0
  22. data/lib/bluepill/dsl/process_factory.rb +122 -0
  23. data/lib/bluepill/dsl/process_proxy.rb +44 -0
  24. data/lib/bluepill/group.rb +72 -0
  25. data/lib/bluepill/process.rb +480 -0
  26. data/lib/bluepill/process_conditions.rb +14 -0
  27. data/lib/bluepill/process_conditions/always_true.rb +18 -0
  28. data/lib/bluepill/process_conditions/cpu_usage.rb +19 -0
  29. data/lib/bluepill/process_conditions/http.rb +58 -0
  30. data/lib/bluepill/process_conditions/mem_usage.rb +32 -0
  31. data/lib/bluepill/process_conditions/process_condition.rb +22 -0
  32. data/lib/bluepill/process_statistics.rb +27 -0
  33. data/lib/bluepill/socket.rb +58 -0
  34. data/lib/bluepill/system.rb +236 -0
  35. data/lib/bluepill/trigger.rb +59 -0
  36. data/lib/bluepill/triggers/flapping.rb +56 -0
  37. data/lib/bluepill/util/rotational_array.rb +20 -0
  38. data/lib/bluepill/version.rb +4 -0
  39. data/spec/lib/bluepill/process_statistics_spec.rb +24 -0
  40. data/spec/lib/bluepill/system_spec.rb +36 -0
  41. data/spec/spec_helper.rb +19 -0
  42. metadata +304 -0
@@ -0,0 +1,59 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Bluepill
3
+ class Trigger
4
+ @implementations = {}
5
+ def self.inherited(klass)
6
+ @implementations[klass.name.split('::').last.underscore.to_sym] = klass
7
+ end
8
+
9
+ def self.[](name)
10
+ @implementations[name]
11
+ end
12
+
13
+ attr_accessor :process, :logger, :mutex, :scheduled_events
14
+
15
+ def initialize(process, options = {})
16
+ self.process = process
17
+ self.logger = options[:logger]
18
+ self.mutex = Mutex.new
19
+ self.scheduled_events = []
20
+ end
21
+
22
+ def reset!
23
+ self.cancel_all_events
24
+ end
25
+
26
+ def notify(transition)
27
+ raise "Implement in subclass"
28
+ end
29
+
30
+ def dispatch!(event)
31
+ self.process.dispatch!(event, self.class.name.split("::").last)
32
+ end
33
+
34
+ def schedule_event(event, delay)
35
+ # TODO: maybe wrap this in a ScheduledEvent class with methods like cancel
36
+ thread = Thread.new(self) do |trigger|
37
+ begin
38
+ sleep delay.to_f
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.exception(e)
45
+ end
46
+ end
47
+
48
+ self.scheduled_events.push([event, thread])
49
+ end
50
+
51
+ def cancel_all_events
52
+ self.logger.info "Canceling all scheduled events"
53
+ self.mutex.synchronize do
54
+ self.scheduled_events.each {|_, thread| thread.kill}
55
+ end
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,56 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Bluepill
3
+ module Triggers
4
+ class Flapping < Bluepill::Trigger
5
+ TRIGGER_STATES = [:starting, :restarting]
6
+
7
+ PARAMS = [:times, :within, :retry_in]
8
+
9
+ attr_accessor *PARAMS
10
+ attr_reader :timeline
11
+
12
+ def initialize(process, options = {})
13
+ options.reverse_merge!(:times => 5, :within => 1, :retry_in => 5)
14
+
15
+ options.each_pair do |name, val|
16
+ instance_variable_set("@#{name}", val) if PARAMS.include?(name)
17
+ end
18
+
19
+ @timeline = Util::RotationalArray.new(@times)
20
+ super
21
+ end
22
+
23
+ def notify(transition)
24
+ if TRIGGER_STATES.include?(transition.to_name)
25
+ self.timeline << Time.now.to_i
26
+ self.check_flapping
27
+ end
28
+ end
29
+
30
+ def reset!
31
+ @timeline.clear
32
+ super
33
+ end
34
+
35
+ def check_flapping
36
+ # The process has not flapped if we haven't encountered enough incidents
37
+ return unless (@timeline.compact.length == self.times)
38
+
39
+ # Check if the incident happend within the timeframe
40
+ duration = (@timeline.last - @timeline.first) <= self.within
41
+
42
+ if duration
43
+ self.logger.info "Flapping detected: retrying in #{self.retry_in} seconds"
44
+
45
+ self.schedule_event(:start, self.retry_in) unless self.retry_in == 0 # retry_in zero means "do not retry, ever"
46
+ self.schedule_event(:unmonitor, 0)
47
+
48
+ @timeline.clear
49
+
50
+ # This will prevent a transition from happening in the process state_machine
51
+ throw :halt
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Bluepill
3
+ module Util
4
+ class RotationalArray < Array
5
+ def initialize(size)
6
+ @capacity = size
7
+
8
+ super() # no size - intentionally
9
+ end
10
+
11
+ def push(value)
12
+ super(value)
13
+
14
+ self.shift if self.length > @capacity
15
+ self
16
+ end
17
+ alias_method :<<, :push
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Bluepill
3
+ VERSION = "0.0.60.1".freeze
4
+ end
@@ -0,0 +1,24 @@
1
+ describe Bluepill::ProcessStatistics do
2
+ before(:each) do
3
+ @stats = Bluepill::ProcessStatistics.new
4
+ end
5
+
6
+ it "should record events" do
7
+ @stats.record_event('some event', 'some reason')
8
+ @stats.record_event('another event', 'another reason')
9
+ @stats.events.should have(2).events
10
+ end
11
+
12
+ it "should record #EVENTS_TO_PERSIST events" do
13
+ (2 * Bluepill::ProcessStatistics::EVENTS_TO_PERSIST).times do
14
+ @stats.record_event('some event', 'some reason')
15
+ end
16
+ @stats.events.should have(Bluepill::ProcessStatistics::EVENTS_TO_PERSIST).events
17
+ end
18
+
19
+ it "should return event history" do
20
+ @stats.record_event('some event', 'some reason')
21
+ @stats.to_s.should match(/some reason/)
22
+ @stats.to_s.should match(/event history/)
23
+ end
24
+ end
@@ -0,0 +1,36 @@
1
+ describe Bluepill::System do
2
+ describe :pid_alive? do
3
+ it "should be true if process responds to zero signal" do
4
+ mock(::Process).kill(0, 555)
5
+ Bluepill::System.should be_pid_alive(555)
6
+ end
7
+
8
+ it "should be false if process throws exception on zero signal" do
9
+ mock(::Process).kill(0, 555) { raise Errno::ESRCH.new }
10
+ Bluepill::System.should_not be_pid_alive(555)
11
+ end
12
+ end
13
+
14
+ describe :store do
15
+ it "should be Hash" do
16
+ Bluepill::System.store.should be_kind_of(Hash)
17
+ end
18
+
19
+ it "should return same Hash or every call" do
20
+ Bluepill::System.store.should be_equal(Bluepill::System.store)
21
+ end
22
+
23
+ it "should store assigned pairs" do
24
+ Bluepill::System.store[:somekey] = 10
25
+ Bluepill::System.store[:somekey].should be_eql(10)
26
+ end
27
+ end
28
+
29
+ describe :reset_data do
30
+ it 'should clear the #store' do
31
+ Bluepill::System.store[:anotherkey] = Faker::Lorem.sentence
32
+ Bluepill::System.reset_data
33
+ Bluepill::System.store.should be_empty
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ if RUBY_VERSION >= '1.9'
2
+ if ENV['ENABLE_SIMPLECOV']
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+ end
6
+ else
7
+ require 'rubygems'
8
+ end
9
+
10
+ require 'faker'
11
+ require 'rspec/core'
12
+
13
+ $LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
14
+
15
+ RSpec.configure do |conf|
16
+ conf.mock_with :rr
17
+ end
18
+
19
+ require 'bluepill'
metadata ADDED
@@ -0,0 +1,304 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kostya-bluepill
3
+ version: !ruby/object:Gem::Version
4
+ hash: 189
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 60
10
+ - 1
11
+ version: 0.0.60.1
12
+ platform: ruby
13
+ authors:
14
+ - Arya Asemanfar
15
+ - Gary Tsang
16
+ - Rohith Ravi
17
+ autorequire:
18
+ bindir: bin
19
+ cert_chain: []
20
+
21
+ date: 2012-08-15 00:00:00 +04:00
22
+ default_executable:
23
+ dependencies:
24
+ - !ruby/object:Gem::Dependency
25
+ name: daemons
26
+ prerelease: false
27
+ requirement: &id001 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ hash: 27
33
+ segments:
34
+ - 1
35
+ - 1
36
+ - 4
37
+ version: 1.1.4
38
+ - - <=
39
+ - !ruby/object:Gem::Version
40
+ hash: 31
41
+ segments:
42
+ - 1
43
+ - 1
44
+ - 6
45
+ version: 1.1.6
46
+ type: :runtime
47
+ version_requirements: *id001
48
+ - !ruby/object:Gem::Dependency
49
+ name: state_machine
50
+ prerelease: false
51
+ requirement: &id002 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ hash: 19
57
+ segments:
58
+ - 1
59
+ - 1
60
+ - 0
61
+ version: 1.1.0
62
+ type: :runtime
63
+ version_requirements: *id002
64
+ - !ruby/object:Gem::Dependency
65
+ name: activesupport
66
+ prerelease: false
67
+ requirement: &id003 !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 7
73
+ segments:
74
+ - 3
75
+ - 0
76
+ - 0
77
+ version: 3.0.0
78
+ type: :runtime
79
+ version_requirements: *id003
80
+ - !ruby/object:Gem::Dependency
81
+ name: i18n
82
+ prerelease: false
83
+ requirement: &id004 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ hash: 11
89
+ segments:
90
+ - 0
91
+ - 5
92
+ - 0
93
+ version: 0.5.0
94
+ type: :runtime
95
+ version_requirements: *id004
96
+ - !ruby/object:Gem::Dependency
97
+ name: madvertise-logging
98
+ prerelease: false
99
+ requirement: &id005 !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ type: :runtime
109
+ version_requirements: *id005
110
+ - !ruby/object:Gem::Dependency
111
+ name: bundler
112
+ prerelease: false
113
+ requirement: &id006 !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ hash: 3
119
+ segments:
120
+ - 1
121
+ - 0
122
+ - 10
123
+ version: 1.0.10
124
+ type: :development
125
+ version_requirements: *id006
126
+ - !ruby/object:Gem::Dependency
127
+ name: rake
128
+ prerelease: false
129
+ requirement: &id007 !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - "!="
133
+ - !ruby/object:Gem::Version
134
+ hash: 59
135
+ segments:
136
+ - 0
137
+ - 9
138
+ - 0
139
+ version: 0.9.0
140
+ type: :development
141
+ version_requirements: *id007
142
+ - !ruby/object:Gem::Dependency
143
+ name: rspec-core
144
+ prerelease: false
145
+ requirement: &id008 !ruby/object:Gem::Requirement
146
+ none: false
147
+ requirements:
148
+ - - ~>
149
+ - !ruby/object:Gem::Version
150
+ hash: 3
151
+ segments:
152
+ - 2
153
+ - 0
154
+ version: "2.0"
155
+ type: :development
156
+ version_requirements: *id008
157
+ - !ruby/object:Gem::Dependency
158
+ name: rspec-expectations
159
+ prerelease: false
160
+ requirement: &id009 !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ~>
164
+ - !ruby/object:Gem::Version
165
+ hash: 3
166
+ segments:
167
+ - 2
168
+ - 0
169
+ version: "2.0"
170
+ type: :development
171
+ version_requirements: *id009
172
+ - !ruby/object:Gem::Dependency
173
+ name: rr
174
+ prerelease: false
175
+ requirement: &id010 !ruby/object:Gem::Requirement
176
+ none: false
177
+ requirements:
178
+ - - ~>
179
+ - !ruby/object:Gem::Version
180
+ hash: 15
181
+ segments:
182
+ - 1
183
+ - 0
184
+ version: "1.0"
185
+ type: :development
186
+ version_requirements: *id010
187
+ - !ruby/object:Gem::Dependency
188
+ name: faker
189
+ prerelease: false
190
+ requirement: &id011 !ruby/object:Gem::Requirement
191
+ none: false
192
+ requirements:
193
+ - - ~>
194
+ - !ruby/object:Gem::Version
195
+ hash: 25
196
+ segments:
197
+ - 0
198
+ - 9
199
+ version: "0.9"
200
+ type: :development
201
+ version_requirements: *id011
202
+ - !ruby/object:Gem::Dependency
203
+ name: yard
204
+ prerelease: false
205
+ requirement: &id012 !ruby/object:Gem::Requirement
206
+ none: false
207
+ requirements:
208
+ - - ~>
209
+ - !ruby/object:Gem::Version
210
+ hash: 5
211
+ segments:
212
+ - 0
213
+ - 7
214
+ version: "0.7"
215
+ type: :development
216
+ version_requirements: *id012
217
+ 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.
218
+ email:
219
+ - entombedvirus@gmail.com
220
+ executables:
221
+ - bluepill
222
+ extensions: []
223
+
224
+ extra_rdoc_files:
225
+ - LICENSE
226
+ - README.md
227
+ files:
228
+ - .gitignore
229
+ - .rspec
230
+ - DESIGN.md
231
+ - Gemfile
232
+ - LICENSE
233
+ - README.md
234
+ - Rakefile
235
+ - bin/bluepill
236
+ - bluepill.gemspec
237
+ - examples/example.rb
238
+ - examples/new_example.rb
239
+ - examples/new_runit_example.rb
240
+ - examples/runit_example.rb
241
+ - lib/bluepill.rb
242
+ - lib/bluepill/application.rb
243
+ - lib/bluepill/application/client.rb
244
+ - lib/bluepill/application/server.rb
245
+ - lib/bluepill/condition_watch.rb
246
+ - lib/bluepill/controller.rb
247
+ - lib/bluepill/dsl.rb
248
+ - lib/bluepill/dsl/app_proxy.rb
249
+ - lib/bluepill/dsl/process_factory.rb
250
+ - lib/bluepill/dsl/process_proxy.rb
251
+ - lib/bluepill/group.rb
252
+ - lib/bluepill/process.rb
253
+ - lib/bluepill/process_conditions.rb
254
+ - lib/bluepill/process_conditions/always_true.rb
255
+ - lib/bluepill/process_conditions/cpu_usage.rb
256
+ - lib/bluepill/process_conditions/http.rb
257
+ - lib/bluepill/process_conditions/mem_usage.rb
258
+ - lib/bluepill/process_conditions/process_condition.rb
259
+ - lib/bluepill/process_statistics.rb
260
+ - lib/bluepill/socket.rb
261
+ - lib/bluepill/system.rb
262
+ - lib/bluepill/trigger.rb
263
+ - lib/bluepill/triggers/flapping.rb
264
+ - lib/bluepill/util/rotational_array.rb
265
+ - lib/bluepill/version.rb
266
+ - spec/lib/bluepill/process_statistics_spec.rb
267
+ - spec/lib/bluepill/system_spec.rb
268
+ - spec/spec_helper.rb
269
+ has_rdoc: true
270
+ homepage: http://github.com/kostya/bluepill
271
+ licenses: []
272
+
273
+ post_install_message:
274
+ rdoc_options: []
275
+
276
+ require_paths:
277
+ - lib
278
+ required_ruby_version: !ruby/object:Gem::Requirement
279
+ none: false
280
+ requirements:
281
+ - - ">="
282
+ - !ruby/object:Gem::Version
283
+ hash: 3
284
+ segments:
285
+ - 0
286
+ version: "0"
287
+ required_rubygems_version: !ruby/object:Gem::Requirement
288
+ none: false
289
+ requirements:
290
+ - - ">="
291
+ - !ruby/object:Gem::Version
292
+ hash: 3
293
+ segments:
294
+ - 0
295
+ version: "0"
296
+ requirements: []
297
+
298
+ rubyforge_project:
299
+ rubygems_version: 1.4.2
300
+ signing_key:
301
+ specification_version: 3
302
+ summary: A process monitor written in Ruby with stability and minimalism in mind.
303
+ test_files: []
304
+