bluepill 0.0.68 → 0.0.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|