gvarela-bluepill 0.0.27
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/DESIGN.md +10 -0
- data/LICENSE +22 -0
- data/README.md +221 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/bin/bluepill +101 -0
- data/bluepill.gemspec +81 -0
- data/lib/bluepill/application/client.rb +7 -0
- data/lib/bluepill/application/server.rb +24 -0
- data/lib/bluepill/application.rb +211 -0
- data/lib/bluepill/condition_watch.rb +55 -0
- data/lib/bluepill/controller.rb +111 -0
- data/lib/bluepill/dsl.rb +140 -0
- data/lib/bluepill/group.rb +40 -0
- data/lib/bluepill/logger.rb +57 -0
- data/lib/bluepill/process.rb +410 -0
- data/lib/bluepill/process_conditions/always_true.rb +17 -0
- data/lib/bluepill/process_conditions/cpu_usage.rb +18 -0
- data/lib/bluepill/process_conditions/mem_usage.rb +31 -0
- data/lib/bluepill/process_conditions/process_condition.rb +17 -0
- data/lib/bluepill/process_conditions.rb +13 -0
- data/lib/bluepill/socket.rb +35 -0
- data/lib/bluepill/system.rb +225 -0
- data/lib/bluepill/trigger.rb +60 -0
- data/lib/bluepill/triggers/flapping.rb +59 -0
- data/lib/bluepill/util/rotational_array.rb +58 -0
- data/lib/bluepill/version.rb +3 -0
- data/lib/bluepill.rb +32 -0
- data/lib/example.rb +81 -0
- metadata +126 -0
@@ -0,0 +1,225 @@
|
|
1
|
+
require 'etc'
|
2
|
+
|
3
|
+
module Bluepill
|
4
|
+
# This class represents the system that bluepill is running on.. It's mainly used to memoize
|
5
|
+
# results of running ps auxx etc so that every watch in the every process will not result in a fork
|
6
|
+
module System
|
7
|
+
APPEND_MODE = "a"
|
8
|
+
extend self
|
9
|
+
|
10
|
+
# The position of each field in ps output
|
11
|
+
IDX_MAP = {
|
12
|
+
:pid => 0,
|
13
|
+
:ppid => 1,
|
14
|
+
:pcpu => 2,
|
15
|
+
:rss => 3
|
16
|
+
}
|
17
|
+
|
18
|
+
def pid_alive?(pid)
|
19
|
+
begin
|
20
|
+
::Process.kill(0, pid)
|
21
|
+
true
|
22
|
+
rescue Errno::ESRCH
|
23
|
+
false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def cpu_usage(pid)
|
28
|
+
ps_axu[pid] && ps_axu[pid][IDX_MAP[:pcpu]].to_f
|
29
|
+
end
|
30
|
+
|
31
|
+
def memory_usage(pid)
|
32
|
+
ps_axu[pid] && ps_axu[pid][IDX_MAP[:rss]].to_f
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_children(parent_pid)
|
36
|
+
returning(Array.new) do |child_pids|
|
37
|
+
ps_axu.each_pair do |pid, chunks|
|
38
|
+
child_pids << chunks[IDX_MAP[:pid]].to_i if chunks[IDX_MAP[:ppid]].to_i == parent_pid.to_i
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the pid of the child that executes the cmd
|
44
|
+
def daemonize(cmd, options = {})
|
45
|
+
rd, wr = IO.pipe
|
46
|
+
|
47
|
+
if child = Daemonize.safefork
|
48
|
+
# we do not wanna create zombies, so detach ourselves from the child exit status
|
49
|
+
::Process.detach(child)
|
50
|
+
|
51
|
+
# parent
|
52
|
+
wr.close
|
53
|
+
|
54
|
+
daemon_id = rd.read.to_i
|
55
|
+
rd.close
|
56
|
+
|
57
|
+
return daemon_id if daemon_id > 0
|
58
|
+
|
59
|
+
else
|
60
|
+
# child
|
61
|
+
rd.close
|
62
|
+
|
63
|
+
drop_privileges(options[:uid], options[:gid])
|
64
|
+
|
65
|
+
# if we cannot write the pid file as the provided user, err out
|
66
|
+
exit unless can_write_pid_file(options[:pid_file], options[:logger])
|
67
|
+
|
68
|
+
to_daemonize = lambda do
|
69
|
+
# Setting end PWD env emulates bash behavior when dealing with symlinks
|
70
|
+
Dir.chdir(ENV["PWD"] = options[:working_dir]) if options[:working_dir]
|
71
|
+
|
72
|
+
redirect_io(*options.values_at(:stdin, :stdout, :stderr))
|
73
|
+
|
74
|
+
::Kernel.exec(cmd)
|
75
|
+
exit
|
76
|
+
end
|
77
|
+
|
78
|
+
daemon_id = Daemonize.call_as_daemon(to_daemonize, nil, cmd)
|
79
|
+
|
80
|
+
File.open(options[:pid_file], "w") {|f| f.write(daemon_id)}
|
81
|
+
|
82
|
+
wr.write daemon_id
|
83
|
+
wr.close
|
84
|
+
|
85
|
+
exit
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns the stdout, stderr and exit code of the cmd
|
90
|
+
def execute_blocking(cmd, options = {})
|
91
|
+
rd, wr = IO.pipe
|
92
|
+
|
93
|
+
if child = Daemonize.safefork
|
94
|
+
# parent
|
95
|
+
wr.close
|
96
|
+
|
97
|
+
cmd_status = rd.read
|
98
|
+
rd.close
|
99
|
+
|
100
|
+
::Process.waitpid(child)
|
101
|
+
|
102
|
+
return Marshal.load(cmd_status)
|
103
|
+
|
104
|
+
else
|
105
|
+
# child
|
106
|
+
rd.close
|
107
|
+
|
108
|
+
# create a child in which we can override the stdin, stdout and stderr
|
109
|
+
cmd_out_read, cmd_out_write = IO.pipe
|
110
|
+
cmd_err_read, cmd_err_write = IO.pipe
|
111
|
+
|
112
|
+
pid = fork {
|
113
|
+
# grandchild
|
114
|
+
drop_privileges(options[:uid], options[:gid])
|
115
|
+
|
116
|
+
Dir.chdir(ENV["PWD"] = options[:working_dir]) if options[:working_dir]
|
117
|
+
|
118
|
+
# close unused fds so ancestors wont hang. This line is the only reason we are not
|
119
|
+
# using something like popen3. If this fd is not closed, the .read call on the parent
|
120
|
+
# will never return because "wr" would still be open in the "exec"-ed cmd
|
121
|
+
wr.close
|
122
|
+
|
123
|
+
# we do not care about stdin of cmd
|
124
|
+
STDIN.reopen("/dev/null")
|
125
|
+
|
126
|
+
# point stdout of cmd to somewhere we can read
|
127
|
+
cmd_out_read.close
|
128
|
+
STDOUT.reopen(cmd_out_write)
|
129
|
+
cmd_out_write.close
|
130
|
+
|
131
|
+
# same thing for stderr
|
132
|
+
cmd_err_read.close
|
133
|
+
STDERR.reopen(cmd_err_write)
|
134
|
+
cmd_err_write.close
|
135
|
+
|
136
|
+
# finally, replace grandchild with cmd
|
137
|
+
::Kernel.exec(cmd)
|
138
|
+
}
|
139
|
+
|
140
|
+
# we do not use these ends of the pipes in the child
|
141
|
+
cmd_out_write.close
|
142
|
+
cmd_err_write.close
|
143
|
+
|
144
|
+
# wait for the cmd to finish executing and acknowledge it's death
|
145
|
+
::Process.waitpid(pid)
|
146
|
+
|
147
|
+
# collect stdout, stderr and exitcode
|
148
|
+
result = {
|
149
|
+
:stdout => cmd_out_read.read,
|
150
|
+
:stderr => cmd_err_read.read,
|
151
|
+
:exit_code => $?.exitstatus
|
152
|
+
}
|
153
|
+
|
154
|
+
# We're done with these ends of the pipes as well
|
155
|
+
cmd_out_read.close
|
156
|
+
cmd_err_read.close
|
157
|
+
|
158
|
+
# Time to tell the parent about what went down
|
159
|
+
wr.write Marshal.dump(result)
|
160
|
+
wr.close
|
161
|
+
|
162
|
+
exit
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def store
|
167
|
+
@store ||= Hash.new
|
168
|
+
end
|
169
|
+
|
170
|
+
def reset_data
|
171
|
+
store.clear unless store.empty?
|
172
|
+
end
|
173
|
+
|
174
|
+
def ps_axu
|
175
|
+
# TODO: need a mutex here
|
176
|
+
store[:ps_axu] ||= begin
|
177
|
+
# BSD style ps invocation
|
178
|
+
lines = `ps axo pid=,ppid=,pcpu=,rss=`.split("\n")
|
179
|
+
|
180
|
+
lines.inject(Hash.new) do |mem, line|
|
181
|
+
chunks = line.split(/\s+/)
|
182
|
+
chunks.delete_if {|c| c.strip.empty? }
|
183
|
+
pid = chunks[IDX_MAP[:pid]].strip.to_i
|
184
|
+
mem[pid] = chunks
|
185
|
+
mem
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# be sure to call this from a fork otherwise it will modify the attributes
|
191
|
+
# of the bluepill daemon
|
192
|
+
def drop_privileges(uid, gid)
|
193
|
+
uid_num = Etc.getpwnam(uid).uid if uid
|
194
|
+
gid_num = Etc.getgrnam(gid).gid if gid
|
195
|
+
|
196
|
+
::Process.groups = [gid_num] if gid
|
197
|
+
::Process::Sys.setgid(gid_num) if gid
|
198
|
+
::Process::Sys.setuid(uid_num) if uid
|
199
|
+
end
|
200
|
+
|
201
|
+
def can_write_pid_file(pid_file, logger)
|
202
|
+
FileUtils.touch(pid_file)
|
203
|
+
File.unlink(pid_file)
|
204
|
+
return true
|
205
|
+
|
206
|
+
rescue Exception => e
|
207
|
+
logger.warning "%s - %s" % [e.class.name, e.message]
|
208
|
+
e.backtrace.each {|l| logger.warning l}
|
209
|
+
return false
|
210
|
+
end
|
211
|
+
|
212
|
+
def redirect_io(io_in, io_out, io_err)
|
213
|
+
$stdin.reopen(io_in) if io_in
|
214
|
+
|
215
|
+
if !io_out.nil? && !io_err.nil? && io_out == io_err
|
216
|
+
$stdout.reopen(io_out, APPEND_MODE)
|
217
|
+
$stderr.reopen($stdout)
|
218
|
+
|
219
|
+
else
|
220
|
+
$stdout.reopen(io_out, APPEND_MODE) if io_out
|
221
|
+
$stderr.reopen(io_err, APPEND_MODE) if io_err
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -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)
|
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.process.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,58 @@
|
|
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
|
+
private
|
52
|
+
|
53
|
+
def rotational_idx(idx)
|
54
|
+
idx % @capacity
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/bluepill.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
require 'thread'
|
3
|
+
require 'monitor'
|
4
|
+
require 'syslog'
|
5
|
+
require 'timeout'
|
6
|
+
require 'logger'
|
7
|
+
|
8
|
+
require 'active_support/inflector'
|
9
|
+
require 'active_support/core_ext/hash'
|
10
|
+
require 'active_support/core_ext/numeric'
|
11
|
+
require 'active_support/core_ext/object/misc'
|
12
|
+
require 'active_support/duration'
|
13
|
+
|
14
|
+
require 'state_machine'
|
15
|
+
|
16
|
+
require 'bluepill/application'
|
17
|
+
require 'bluepill/controller'
|
18
|
+
require 'bluepill/socket'
|
19
|
+
require "bluepill/process"
|
20
|
+
require "bluepill/group"
|
21
|
+
require "bluepill/logger"
|
22
|
+
require "bluepill/condition_watch"
|
23
|
+
require 'bluepill/trigger'
|
24
|
+
require 'bluepill/triggers/flapping'
|
25
|
+
require "bluepill/dsl"
|
26
|
+
require "bluepill/system"
|
27
|
+
|
28
|
+
require "bluepill/process_conditions"
|
29
|
+
|
30
|
+
require "bluepill/util/rotational_array"
|
31
|
+
|
32
|
+
require "bluepill/version"
|
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
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gvarela-bluepill
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.27
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Arya Asemanfar
|
8
|
+
- Gary Tsang
|
9
|
+
- Rohith Ravi
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
|
14
|
+
date: 2009-12-04 00:00:00 -07:00
|
15
|
+
default_executable: bluepill
|
16
|
+
dependencies:
|
17
|
+
- !ruby/object:Gem::Dependency
|
18
|
+
name: daemons
|
19
|
+
type: :runtime
|
20
|
+
version_requirement:
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 1.0.9
|
26
|
+
version:
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: blankslate
|
29
|
+
type: :runtime
|
30
|
+
version_requirement:
|
31
|
+
version_requirements: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 2.1.2.2
|
36
|
+
version:
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: state_machine
|
39
|
+
type: :runtime
|
40
|
+
version_requirement:
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.8.0
|
46
|
+
version:
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: activesupport
|
49
|
+
type: :runtime
|
50
|
+
version_requirement:
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 2.3.5
|
56
|
+
version:
|
57
|
+
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.
|
58
|
+
email: entombedvirus@gmail.com
|
59
|
+
executables:
|
60
|
+
- bluepill
|
61
|
+
extensions: []
|
62
|
+
|
63
|
+
extra_rdoc_files:
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
files:
|
67
|
+
- .gitignore
|
68
|
+
- DESIGN.md
|
69
|
+
- LICENSE
|
70
|
+
- README.md
|
71
|
+
- Rakefile
|
72
|
+
- VERSION
|
73
|
+
- bin/bluepill
|
74
|
+
- bluepill.gemspec
|
75
|
+
- lib/bluepill.rb
|
76
|
+
- lib/bluepill/application.rb
|
77
|
+
- lib/bluepill/application/client.rb
|
78
|
+
- lib/bluepill/application/server.rb
|
79
|
+
- lib/bluepill/condition_watch.rb
|
80
|
+
- lib/bluepill/controller.rb
|
81
|
+
- lib/bluepill/dsl.rb
|
82
|
+
- lib/bluepill/group.rb
|
83
|
+
- lib/bluepill/logger.rb
|
84
|
+
- lib/bluepill/process.rb
|
85
|
+
- lib/bluepill/process_conditions.rb
|
86
|
+
- lib/bluepill/process_conditions/always_true.rb
|
87
|
+
- lib/bluepill/process_conditions/cpu_usage.rb
|
88
|
+
- lib/bluepill/process_conditions/mem_usage.rb
|
89
|
+
- lib/bluepill/process_conditions/process_condition.rb
|
90
|
+
- lib/bluepill/socket.rb
|
91
|
+
- lib/bluepill/system.rb
|
92
|
+
- lib/bluepill/trigger.rb
|
93
|
+
- lib/bluepill/triggers/flapping.rb
|
94
|
+
- lib/bluepill/util/rotational_array.rb
|
95
|
+
- lib/bluepill/version.rb
|
96
|
+
- lib/example.rb
|
97
|
+
has_rdoc: true
|
98
|
+
homepage: http://github.com/gvarela/bluepill
|
99
|
+
licenses: []
|
100
|
+
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options:
|
103
|
+
- --charset=UTF-8
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: "0"
|
111
|
+
version:
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: "0"
|
117
|
+
version:
|
118
|
+
requirements: []
|
119
|
+
|
120
|
+
rubyforge_project:
|
121
|
+
rubygems_version: 1.3.5
|
122
|
+
signing_key:
|
123
|
+
specification_version: 3
|
124
|
+
summary: A process monitor written in Ruby with stability and minimalism in mind.
|
125
|
+
test_files: []
|
126
|
+
|