bluepill 0.0.68 → 0.0.69
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -2
- data/bin/bluepill +19 -20
- data/bin/sample_forking_server +26 -13
- data/bluepill.gemspec +12 -18
- data/lib/bluepill.rb +12 -12
- data/lib/bluepill/application.rb +64 -71
- data/lib/bluepill/application/client.rb +0 -2
- data/lib/bluepill/application/server.rb +1 -3
- data/lib/bluepill/condition_watch.rb +12 -7
- data/lib/bluepill/controller.rb +37 -42
- data/lib/bluepill/dsl.rb +1 -2
- data/lib/bluepill/dsl/app_proxy.rb +3 -4
- data/lib/bluepill/dsl/process_factory.rb +40 -44
- data/lib/bluepill/dsl/process_proxy.rb +4 -5
- data/lib/bluepill/group.rb +15 -21
- data/lib/bluepill/logger.rb +4 -4
- data/lib/bluepill/process.rb +107 -109
- data/lib/bluepill/process_conditions.rb +1 -3
- data/lib/bluepill/process_conditions/always_true.rb +2 -3
- data/lib/bluepill/process_conditions/cpu_usage.rb +0 -1
- data/lib/bluepill/process_conditions/file_time.rb +5 -6
- data/lib/bluepill/process_conditions/http.rb +11 -9
- data/lib/bluepill/process_conditions/mem_usage.rb +6 -7
- data/lib/bluepill/process_conditions/process_condition.rb +4 -5
- data/lib/bluepill/process_conditions/running_time.rb +1 -2
- data/lib/bluepill/process_conditions/zombie_process.rb +1 -2
- data/lib/bluepill/process_journal.rb +18 -21
- data/lib/bluepill/process_statistics.rb +2 -4
- data/lib/bluepill/socket.rb +13 -16
- data/lib/bluepill/system.rb +57 -63
- data/lib/bluepill/trigger.rb +9 -11
- data/lib/bluepill/triggers/flapping.rb +12 -16
- data/lib/bluepill/util/rotational_array.rb +1 -2
- data/lib/bluepill/version.rb +1 -2
- metadata +4 -28
- data/.gitignore +0 -12
- data/.rspec +0 -1
- data/.travis.yml +0 -17
- data/Gemfile +0 -27
- data/Rakefile +0 -38
- data/examples/example.rb +0 -87
- data/examples/new_example.rb +0 -89
- data/examples/new_runit_example.rb +0 -29
- data/examples/runit_example.rb +0 -26
- data/local-bluepill +0 -130
- data/spec/lib/bluepill/application_spec.rb +0 -51
- data/spec/lib/bluepill/logger_spec.rb +0 -3
- data/spec/lib/bluepill/process_spec.rb +0 -135
- data/spec/lib/bluepill/process_statistics_spec.rb +0 -24
- data/spec/lib/bluepill/system_spec.rb +0 -45
- data/spec/spec_helper.rb +0 -26
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
module Bluepill
|
3
2
|
class HistoryValue < Struct.new(:value, :critical)
|
4
3
|
end
|
@@ -11,9 +10,9 @@ module Bluepill
|
|
11
10
|
@name = name
|
12
11
|
|
13
12
|
@logger = options.delete(:logger)
|
14
|
-
@fires = options.
|
13
|
+
@fires = options.key?(:fires) ? Array(options.delete(:fires)) : [:restart]
|
15
14
|
@every = options.delete(:every)
|
16
|
-
@times = options.delete(:times) || [1,1]
|
15
|
+
@times = options.delete(:times) || [1, 1]
|
17
16
|
@times = [@times, @times] unless @times.is_a?(Array) # handles :times => 5
|
18
17
|
@include_children = options.delete(:include_children) || false
|
19
18
|
|
@@ -26,9 +25,15 @@ module Bluepill
|
|
26
25
|
if @last_ran_at.nil? || (@last_ran_at + @every) <= tick_number
|
27
26
|
@last_ran_at = tick_number
|
28
27
|
|
29
|
-
|
28
|
+
begin
|
29
|
+
value = @process_condition.run(pid, @include_children)
|
30
|
+
rescue => e
|
31
|
+
logger.err(e.backtrace)
|
32
|
+
raise e
|
33
|
+
end
|
34
|
+
|
30
35
|
@history << HistoryValue.new(@process_condition.format_value(value), @process_condition.check(value))
|
31
|
-
|
36
|
+
logger.info(to_s)
|
32
37
|
|
33
38
|
return @fires if self.fired?
|
34
39
|
end
|
@@ -40,11 +45,11 @@ module Bluepill
|
|
40
45
|
end
|
41
46
|
|
42
47
|
def fired?
|
43
|
-
@history.count {|v|
|
48
|
+
@history.count { |v| !v.critical } >= @times.first
|
44
49
|
end
|
45
50
|
|
46
51
|
def to_s
|
47
|
-
data = @history.collect {|v| "#{v.value}#{'*' unless v.critical}"}.join(
|
52
|
+
data = @history.collect { |v| "#{v.value}#{'*' unless v.critical}" }.join(', ')
|
48
53
|
"#{@name}: [#{data}]\n"
|
49
54
|
end
|
50
55
|
end
|
data/lib/bluepill/controller.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
require 'fileutils'
|
3
2
|
require 'bluepill/system'
|
4
3
|
|
@@ -17,18 +16,18 @@ module Bluepill
|
|
17
16
|
end
|
18
17
|
|
19
18
|
def running_applications
|
20
|
-
Dir[File.join(sockets_dir,
|
19
|
+
Dir[File.join(sockets_dir, '*.sock')].map { |x| File.basename(x, '.sock') }
|
21
20
|
end
|
22
21
|
|
23
22
|
def handle_command(application, command, *args)
|
24
23
|
case command.to_sym
|
25
24
|
when :status
|
26
|
-
puts
|
25
|
+
puts send_to_daemon(application, :status, *args)
|
27
26
|
when *Application::PROCESS_COMMANDS
|
28
27
|
# these need to be sent to the daemon and the results printed out
|
29
|
-
affected =
|
28
|
+
affected = send_to_daemon(application, command, *args)
|
30
29
|
if affected.empty?
|
31
|
-
puts
|
30
|
+
puts 'No processes effected'
|
32
31
|
else
|
33
32
|
puts "Sent #{command} to:"
|
34
33
|
affected.each do |process|
|
@@ -38,14 +37,14 @@ module Bluepill
|
|
38
37
|
when :quit
|
39
38
|
pid = pid_for(application)
|
40
39
|
if System.pid_alive?(pid)
|
41
|
-
::Process.kill(
|
40
|
+
::Process.kill('TERM', pid)
|
42
41
|
puts "Killing bluepilld[#{pid}]"
|
43
42
|
else
|
44
43
|
puts "bluepilld[#{pid}] not running"
|
45
44
|
end
|
46
45
|
when :log
|
47
|
-
log_file_location =
|
48
|
-
log_file_location =
|
46
|
+
log_file_location = send_to_daemon(application, :log_file)
|
47
|
+
log_file_location = log_file if log_file_location.to_s.strip.empty?
|
49
48
|
|
50
49
|
requested_pattern = args.first
|
51
50
|
grep_pattern = self.grep_pattern(application, requested_pattern)
|
@@ -54,69 +53,65 @@ module Bluepill
|
|
54
53
|
puts "Tailing log for #{requested_pattern}..."
|
55
54
|
Kernel.exec(tail)
|
56
55
|
else
|
57
|
-
$stderr.puts
|
56
|
+
$stderr.puts(format('Unknown command `%s` (or application `%s` has not been loaded yet)', command, command))
|
58
57
|
exit(1)
|
59
58
|
end
|
60
59
|
end
|
61
60
|
|
62
61
|
def send_to_daemon(application, command, *args)
|
63
|
-
|
64
|
-
verify_version!(application)
|
65
|
-
|
66
|
-
command = ([command, *args]).join(":")
|
67
|
-
response = Socket.client_command(base_dir, application, command)
|
68
|
-
if response.is_a?(Exception)
|
69
|
-
$stderr.puts "Received error from server:"
|
70
|
-
$stderr.puts response.inspect
|
71
|
-
$stderr.puts response.backtrace.join("\n")
|
72
|
-
exit(8)
|
73
|
-
else
|
74
|
-
response
|
75
|
-
end
|
62
|
+
verify_version!(application)
|
76
63
|
|
77
|
-
|
78
|
-
|
64
|
+
command = ([command, *args]).join(':')
|
65
|
+
response = Socket.client_command(base_dir, application, command)
|
66
|
+
if response.is_a?(Exception)
|
67
|
+
$stderr.puts 'Received error from server:'
|
68
|
+
$stderr.puts response.inspect
|
69
|
+
$stderr.puts response.backtrace.join("\n")
|
70
|
+
exit(8)
|
71
|
+
else
|
72
|
+
response
|
79
73
|
end
|
74
|
+
|
75
|
+
rescue Errno::ECONNREFUSED
|
76
|
+
abort('Connection Refused: Server is not running')
|
80
77
|
end
|
81
78
|
|
82
79
|
def grep_pattern(application, query = nil)
|
83
80
|
pattern = [application, query].compact.join(':')
|
84
81
|
['\[.*', Regexp.escape(pattern), '.*'].compact.join
|
85
82
|
end
|
86
|
-
|
83
|
+
|
84
|
+
private
|
87
85
|
|
88
86
|
def cleanup_bluepill_directory
|
89
|
-
|
87
|
+
running_applications.each do |app|
|
90
88
|
pid = pid_for(app)
|
91
|
-
if
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
89
|
+
next if pid || System.pid_alive?(pid)
|
90
|
+
pid_file = File.join(pids_dir, "#{app}.pid")
|
91
|
+
sock_file = File.join(sockets_dir, "#{app}.sock")
|
92
|
+
System.delete_if_exists(pid_file)
|
93
|
+
System.delete_if_exists(sock_file)
|
97
94
|
end
|
98
95
|
end
|
99
96
|
|
100
97
|
def pid_for(app)
|
101
|
-
pid_file = File.join(
|
102
|
-
File.
|
98
|
+
pid_file = File.join(pids_dir, "#{app}.pid")
|
99
|
+
File.exist?(pid_file) && File.read(pid_file).to_i
|
103
100
|
end
|
104
101
|
|
105
102
|
def setup_dir_structure
|
106
103
|
[@sockets_dir, @pids_dir].each do |dir|
|
107
|
-
FileUtils.mkdir_p(dir) unless File.
|
104
|
+
FileUtils.mkdir_p(dir) unless File.exist?(dir)
|
108
105
|
end
|
109
106
|
end
|
110
107
|
|
111
108
|
def verify_version!(application)
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
abort("The running version of your daemon seems to be out of date.\nDaemon Version: #{version}, CLI Version: #{Bluepill::VERSION}")
|
116
|
-
end
|
117
|
-
rescue ArgumentError
|
118
|
-
abort("The running version of your daemon seems to be out of date.")
|
109
|
+
version = Socket.client_command(base_dir, application, 'version')
|
110
|
+
if version != Bluepill::VERSION
|
111
|
+
abort("The running version of your daemon seems to be out of date.\nDaemon Version: #{version}, CLI Version: #{Bluepill::VERSION}")
|
119
112
|
end
|
113
|
+
rescue ArgumentError
|
114
|
+
abort('The running version of your daemon seems to be out of date.')
|
120
115
|
end
|
121
116
|
end
|
122
117
|
end
|
data/lib/bluepill/dsl.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
module Bluepill
|
3
2
|
def self.application(app_name, options = {}, &block)
|
4
3
|
app_proxy = AppProxy.new(app_name, options)
|
5
4
|
if block.arity == 0
|
6
|
-
app_proxy.instance_eval
|
5
|
+
app_proxy.instance_eval(&block)
|
7
6
|
else
|
8
7
|
app_proxy.instance_exec(app_proxy, &block)
|
9
8
|
end
|
@@ -1,9 +1,8 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
module Bluepill
|
3
2
|
class AppProxy
|
4
|
-
APP_ATTRIBUTES = [:working_dir, :uid, :gid, :environment, :auto_start
|
3
|
+
APP_ATTRIBUTES = [:working_dir, :uid, :gid, :environment, :auto_start]
|
5
4
|
|
6
|
-
attr_accessor
|
5
|
+
attr_accessor(*APP_ATTRIBUTES)
|
7
6
|
attr_reader :app
|
8
7
|
|
9
8
|
def initialize(app_name, options)
|
@@ -12,7 +11,7 @@ module Bluepill
|
|
12
11
|
|
13
12
|
def process(process_name, &process_block)
|
14
13
|
attributes = {}
|
15
|
-
APP_ATTRIBUTES.each { |a| attributes[a] =
|
14
|
+
APP_ATTRIBUTES.each { |a| attributes[a] = send(a) }
|
16
15
|
|
17
16
|
process_factory = ProcessFactory.new(attributes, process_block)
|
18
17
|
|
@@ -1,10 +1,9 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
module Bluepill
|
3
2
|
class ProcessFactory
|
4
3
|
attr_reader :attributes
|
5
4
|
|
6
|
-
@@process_keys =
|
7
|
-
@@pid_files =
|
5
|
+
@@process_keys = {}
|
6
|
+
@@pid_files = {}
|
8
7
|
|
9
8
|
def initialize(attributes, process_block)
|
10
9
|
@attributes = attributes
|
@@ -12,7 +11,7 @@ module Bluepill
|
|
12
11
|
end
|
13
12
|
|
14
13
|
def create_process(name, pids_dir)
|
15
|
-
|
14
|
+
assign_default_pid_file(name, pids_dir)
|
16
15
|
|
17
16
|
process = ProcessProxy.new(name, @attributes, @process_block)
|
18
17
|
child_process_block = @attributes.delete(:child_process_block)
|
@@ -24,7 +23,7 @@ module Bluepill
|
|
24
23
|
|
25
24
|
def create_child_process(name, pid, logger)
|
26
25
|
attributes = {}
|
27
|
-
[:start_grace_time, :stop_grace_time, :restart_grace_time].each {|a| attributes[a] = @attributes[a]}
|
26
|
+
[:start_grace_time, :stop_grace_time, :restart_grace_time].each { |a| attributes[a] = @attributes[a] }
|
28
27
|
attributes[:actual_pid] = pid
|
29
28
|
attributes[:logger] = logger
|
30
29
|
|
@@ -36,19 +35,18 @@ module Bluepill
|
|
36
35
|
process
|
37
36
|
end
|
38
37
|
|
39
|
-
|
38
|
+
protected
|
40
39
|
|
41
40
|
def assign_default_pid_file(process_name, pids_dir)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
41
|
+
return if @attributes.key?(:pid_file)
|
42
|
+
group_name = @attributes[:group]
|
43
|
+
default_pid_name = [group_name, process_name].compact.join('_').gsub(/[^A-Za-z0-9_\-]/, '_')
|
44
|
+
@attributes[:pid_file] = File.join(pids_dir, default_pid_name + '.pid')
|
47
45
|
end
|
48
46
|
|
49
47
|
def validate_process!(process)
|
50
48
|
# validate uniqueness of group:process
|
51
|
-
process_key = [process.attributes[:group], process.name].join(
|
49
|
+
process_key = [process.attributes[:group], process.name].join(':')
|
52
50
|
if @@process_keys.key?(process_key)
|
53
51
|
$stderr.print "Config Error: You have two entries for the process name '#{process.name}'"
|
54
52
|
$stderr.print " in the group '#{process.attributes[:group]}'" if process.attributes.key?(:group)
|
@@ -60,7 +58,7 @@ module Bluepill
|
|
60
58
|
|
61
59
|
# validate required attributes
|
62
60
|
[:start_command].each do |required_attr|
|
63
|
-
|
61
|
+
unless process.attributes.key?(required_attr)
|
64
62
|
$stderr.puts "Config Error: You must specify a #{required_attr} for '#{process.name}'"
|
65
63
|
exit(6)
|
66
64
|
end
|
@@ -75,48 +73,46 @@ module Bluepill
|
|
75
73
|
@@pid_files[pid_key] = 0
|
76
74
|
end
|
77
75
|
|
78
|
-
#validate stop_signals array
|
76
|
+
# validate stop_signals array
|
79
77
|
stop_grace_time = process.attributes[:stop_grace_time]
|
80
78
|
stop_signals = process.attributes[:stop_signals]
|
81
79
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
98
|
-
delay_sum += delay
|
80
|
+
return if stop_signals.nil?
|
81
|
+
# Start with the more helpful error messages before the 'odd number' message.
|
82
|
+
delay_sum = 0
|
83
|
+
stop_signals.each_with_index do |s_or_d, i|
|
84
|
+
if i.even?
|
85
|
+
signal = s_or_d
|
86
|
+
unless signal.is_a? Symbol
|
87
|
+
$stderr.puts "Config Error: Invalid stop_signals! Expected a symbol (signal) at position #{i} instead of '#{signal}'."
|
88
|
+
exit(6)
|
89
|
+
end
|
90
|
+
else
|
91
|
+
delay = s_or_d
|
92
|
+
unless delay.is_a? Fixnum
|
93
|
+
$stderr.puts "Config Error: Invalid stop_signals! Expected a number (delay) at position #{i} instead of '#{delay}'."
|
94
|
+
exit(6)
|
99
95
|
end
|
96
|
+
delay_sum += delay
|
100
97
|
end
|
98
|
+
end
|
101
99
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
100
|
+
unless stop_signals.size.odd?
|
101
|
+
$stderr.puts 'Config Error: Invalid stop_signals! Expected an odd number of elements.'
|
102
|
+
exit(6)
|
103
|
+
end
|
106
104
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
end
|
105
|
+
if stop_grace_time.nil? || stop_grace_time <= delay_sum
|
106
|
+
$stderr.puts 'Config Error: Stop_grace_time should be greater than the sum of stop_signals delays!'
|
107
|
+
exit(6)
|
111
108
|
end
|
112
109
|
end
|
113
110
|
|
114
111
|
def validate_child_process!(child)
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
112
|
+
return if child.attributes.key?(:stop_command)
|
113
|
+
$stderr.puts "Config Error: Invalid child process monitor for #{child.name}"
|
114
|
+
$stderr.puts 'You must specify a stop command to monitor child processes.'
|
115
|
+
exit(6)
|
120
116
|
end
|
121
117
|
end
|
122
118
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
module Bluepill
|
3
2
|
class ProcessProxy
|
4
3
|
attr_reader :attributes, :watches, :name
|
@@ -7,8 +6,8 @@ module Bluepill
|
|
7
6
|
@attributes = attributes
|
8
7
|
@watches = {}
|
9
8
|
|
10
|
-
if process_block.arity
|
11
|
-
instance_eval
|
9
|
+
if process_block.arity.zero?
|
10
|
+
instance_eval(&process_block)
|
12
11
|
else
|
13
12
|
instance_exec(self, &process_block)
|
14
13
|
end
|
@@ -16,11 +15,11 @@ module Bluepill
|
|
16
15
|
|
17
16
|
def method_missing(name, *args)
|
18
17
|
if args.size == 1 && name.to_s =~ /^(.*)=$/
|
19
|
-
@attributes[
|
18
|
+
@attributes[Regexp.last_match[1].to_sym] = args.first
|
20
19
|
elsif args.size == 1
|
21
20
|
@attributes[name.to_sym] = args.first
|
22
21
|
elsif args.size == 0 && name.to_s =~ /^(.*)!$/
|
23
|
-
@attributes[
|
22
|
+
@attributes[Regexp.last_match[1].to_sym] = true
|
24
23
|
elsif args.empty? && @attributes.key?(name.to_sym)
|
25
24
|
@attributes[name.to_sym]
|
26
25
|
else
|
data/lib/bluepill/group.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
module Bluepill
|
3
2
|
class Group
|
4
3
|
attr_accessor :name, :processes, :logger
|
@@ -11,20 +10,16 @@ module Bluepill
|
|
11
10
|
end
|
12
11
|
|
13
12
|
def add_process(process)
|
14
|
-
process.logger =
|
15
|
-
|
13
|
+
process.logger = logger.prefix_with(process.name)
|
14
|
+
processes << process
|
16
15
|
end
|
17
16
|
|
18
17
|
def tick
|
19
|
-
|
20
|
-
process.tick
|
21
|
-
end
|
18
|
+
processes.each(&:tick)
|
22
19
|
end
|
23
20
|
|
24
21
|
def determine_initial_state
|
25
|
-
|
26
|
-
process.determine_initial_state
|
27
|
-
end
|
22
|
+
processes.each(&:determine_initial_state)
|
28
23
|
end
|
29
24
|
|
30
25
|
# proxied events
|
@@ -55,26 +50,25 @@ module Bluepill
|
|
55
50
|
def status(process_name = nil)
|
56
51
|
lines = []
|
57
52
|
if process_name.nil?
|
58
|
-
prefix =
|
59
|
-
lines << "#{
|
53
|
+
prefix = name ? ' ' : ''
|
54
|
+
lines << "#{name}:" if name
|
60
55
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
end
|
56
|
+
processes.each do |process|
|
57
|
+
next unless process.monitor_children?
|
58
|
+
lines << format('%s%s(pid:%s): %s', prefix, process.name, process.actual_pid, process.state)
|
59
|
+
process.children.each do |child|
|
60
|
+
lines << format(' %s%s: %s', prefix, child.name, child.state)
|
67
61
|
end
|
68
62
|
end
|
63
|
+
|
69
64
|
else
|
70
|
-
|
65
|
+
processes.each do |process|
|
71
66
|
next if process_name != process.name
|
72
|
-
lines <<
|
67
|
+
lines << format('%s%s(pid:%s): %s', prefix, process.name, process.actual_pid, process.state)
|
73
68
|
lines << process.statistics.to_s
|
74
69
|
end
|
75
70
|
end
|
76
|
-
lines <<
|
71
|
+
lines << ''
|
77
72
|
end
|
78
|
-
|
79
73
|
end
|
80
74
|
end
|