ra-bluepill 0.0.44

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,72 @@
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
+ unless method_defined?(:nitems)
60
+ def nitems
61
+ compact.length
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def rotational_idx(idx)
68
+ idx % @capacity
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,3 @@
1
+ module Bluepill
2
+ VERSION = "0.0.44"
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
@@ -0,0 +1,82 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ra-bluepill}
8
+ s.version = "0.0.44"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Arya Asemanfar", "Gary Tsang", "Rohith Ravi"]
12
+ s.date = %q{2011-02-09}
13
+ s.default_executable = %q{bluepill}
14
+ s.description = %q{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.}
15
+ s.email = %q{entombedvirus@gmail.com}
16
+ s.executables = ["bluepill"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.md"
20
+ ]
21
+ s.files = [
22
+ "DESIGN.md",
23
+ "LICENSE",
24
+ "README.md",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "bin/bluepill",
28
+ "bin/bpsv",
29
+ "lib/bluepill.rb",
30
+ "lib/bluepill/application.rb",
31
+ "lib/bluepill/application/client.rb",
32
+ "lib/bluepill/application/server.rb",
33
+ "lib/bluepill/condition_watch.rb",
34
+ "lib/bluepill/controller.rb",
35
+ "lib/bluepill/dsl.rb",
36
+ "lib/bluepill/group.rb",
37
+ "lib/bluepill/logger.rb",
38
+ "lib/bluepill/process.rb",
39
+ "lib/bluepill/process_conditions.rb",
40
+ "lib/bluepill/process_conditions/always_true.rb",
41
+ "lib/bluepill/process_conditions/cpu_usage.rb",
42
+ "lib/bluepill/process_conditions/http.rb",
43
+ "lib/bluepill/process_conditions/mem_usage.rb",
44
+ "lib/bluepill/process_conditions/process_condition.rb",
45
+ "lib/bluepill/process_statistics.rb",
46
+ "lib/bluepill/socket.rb",
47
+ "lib/bluepill/system.rb",
48
+ "lib/bluepill/trigger.rb",
49
+ "lib/bluepill/triggers/flapping.rb",
50
+ "lib/bluepill/util/rotational_array.rb",
51
+ "lib/bluepill/version.rb",
52
+ "lib/example.rb",
53
+ "lib/runit_example.rb",
54
+ "ra-bluepill.gemspec"
55
+ ]
56
+ s.homepage = %q{http://github.com/arya/bluepill}
57
+ s.require_paths = ["lib"]
58
+ s.rubygems_version = %q{1.5.0}
59
+ s.summary = %q{A process monitor written in Ruby with stability and minimalism in mind.}
60
+
61
+ if s.respond_to? :specification_version then
62
+ s.specification_version = 3
63
+
64
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
65
+ s.add_runtime_dependency(%q<daemons>, [">= 1.0.9"])
66
+ s.add_runtime_dependency(%q<blankslate>, [">= 2.1.2.2"])
67
+ s.add_runtime_dependency(%q<state_machine>, [">= 0.8.0"])
68
+ s.add_runtime_dependency(%q<activesupport>, [">= 2.3.4"])
69
+ else
70
+ s.add_dependency(%q<daemons>, [">= 1.0.9"])
71
+ s.add_dependency(%q<blankslate>, [">= 2.1.2.2"])
72
+ s.add_dependency(%q<state_machine>, [">= 0.8.0"])
73
+ s.add_dependency(%q<activesupport>, [">= 2.3.4"])
74
+ end
75
+ else
76
+ s.add_dependency(%q<daemons>, [">= 1.0.9"])
77
+ s.add_dependency(%q<blankslate>, [">= 2.1.2.2"])
78
+ s.add_dependency(%q<state_machine>, [">= 0.8.0"])
79
+ s.add_dependency(%q<activesupport>, [">= 2.3.4"])
80
+ end
81
+ end
82
+
metadata ADDED
@@ -0,0 +1,166 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ra-bluepill
3
+ version: !ruby/object:Gem::Version
4
+ hash: 71
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 44
10
+ version: 0.0.44
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: 2011-02-09 00:00:00 -02: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: entombedvirus@gmail.com
90
+ executables:
91
+ - bluepill
92
+ extensions: []
93
+
94
+ extra_rdoc_files:
95
+ - LICENSE
96
+ - README.md
97
+ files:
98
+ - DESIGN.md
99
+ - LICENSE
100
+ - README.md
101
+ - Rakefile
102
+ - VERSION
103
+ - bin/bluepill
104
+ - bin/bpsv
105
+ - lib/bluepill.rb
106
+ - lib/bluepill/application.rb
107
+ - lib/bluepill/application/client.rb
108
+ - lib/bluepill/application/server.rb
109
+ - lib/bluepill/condition_watch.rb
110
+ - lib/bluepill/controller.rb
111
+ - lib/bluepill/dsl.rb
112
+ - lib/bluepill/group.rb
113
+ - lib/bluepill/logger.rb
114
+ - lib/bluepill/process.rb
115
+ - lib/bluepill/process_conditions.rb
116
+ - lib/bluepill/process_conditions/always_true.rb
117
+ - lib/bluepill/process_conditions/cpu_usage.rb
118
+ - lib/bluepill/process_conditions/http.rb
119
+ - lib/bluepill/process_conditions/mem_usage.rb
120
+ - lib/bluepill/process_conditions/process_condition.rb
121
+ - lib/bluepill/process_statistics.rb
122
+ - lib/bluepill/socket.rb
123
+ - lib/bluepill/system.rb
124
+ - lib/bluepill/trigger.rb
125
+ - lib/bluepill/triggers/flapping.rb
126
+ - lib/bluepill/util/rotational_array.rb
127
+ - lib/bluepill/version.rb
128
+ - lib/example.rb
129
+ - lib/runit_example.rb
130
+ - ra-bluepill.gemspec
131
+ has_rdoc: true
132
+ homepage: http://github.com/arya/bluepill
133
+ licenses: []
134
+
135
+ post_install_message:
136
+ rdoc_options: []
137
+
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ hash: 3
146
+ segments:
147
+ - 0
148
+ version: "0"
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ hash: 3
155
+ segments:
156
+ - 0
157
+ version: "0"
158
+ requirements: []
159
+
160
+ rubyforge_project:
161
+ rubygems_version: 1.5.0
162
+ signing_key:
163
+ specification_version: 3
164
+ summary: A process monitor written in Ruby with stability and minimalism in mind.
165
+ test_files: []
166
+