bluepill 0.0.21 → 0.0.22
Sign up to get free protection for your applications and to get access to all the features.
- data/{DESIGN.markdown → DESIGN.md} +0 -0
- data/LICENSE +21 -2
- data/{README.markdown → README.md} +48 -3
- data/Rakefile +0 -15
- data/VERSION +1 -1
- data/bin/bluepill +41 -15
- data/bluepill.gemspec +10 -20
- data/lib/bluepill.rb +1 -0
- data/lib/bluepill/application.rb +23 -11
- data/lib/bluepill/controller.rb +4 -17
- data/lib/bluepill/dsl.rb +41 -2
- data/lib/bluepill/logger.rb +30 -2
- data/lib/bluepill/socket.rb +2 -4
- data/lib/example.rb +6 -5
- metadata +8 -14
- data/.document +0 -5
- data/spec/blue-pill_spec.rb +0 -7
- data/spec/process_spec.rb +0 -33
- data/spec/spec_helper.rb +0 -13
File without changes
|
data/LICENSE
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
-
Copyright (c) 2009
|
2
|
-
license tbd
|
1
|
+
Copyright (c) 2009 Arya Asemanfar, Rohith Ravi, Gary Tsang
|
3
2
|
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
@@ -6,7 +6,7 @@ It's hosted on [gemcutter.org][gemcutter].
|
|
6
6
|
|
7
7
|
sudo gem install bluepill
|
8
8
|
|
9
|
-
In order to take advantage of logging, you also need to setup your syslog to log the local6 facility. Edit the appropriate config file for your syslogger (/etc/syslog.conf for syslog) and add a line for local6:
|
9
|
+
In order to take advantage of logging with syslog, you also need to setup your syslog to log the local6 facility. Edit the appropriate config file for your syslogger (/etc/syslog.conf for syslog) and add a line for local6:
|
10
10
|
|
11
11
|
local6.* /var/log/bluepill.log
|
12
12
|
|
@@ -15,7 +15,7 @@ You'll also want to add _/var/log/bluepill.log_ to _/etc/logrotate.d/syslog_ so
|
|
15
15
|
Lastly, create the _/var/bluepill_ directory for bluepill to store its pid and sock files.
|
16
16
|
|
17
17
|
## Usage
|
18
|
-
###
|
18
|
+
### Config
|
19
19
|
Bluepill organizes processes into 3 levels: application -> group -> process. Each process has a few attributes that tell bluepill how to start, stop, and restart it, where to look or put the pid file, what process conditions to monitor and the options for each of those.
|
20
20
|
|
21
21
|
The minimum config file looks something like this:
|
@@ -105,9 +105,44 @@ If you want to run the process as someone other than root:
|
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
108
|
+
You can also set an app-wide uid/gid:
|
109
|
+
|
110
|
+
Bluepill.application("app_name") do |app|
|
111
|
+
app.uid = "deploy"
|
112
|
+
app.gid = "deploy"
|
113
|
+
app.process("process_name") do |process|
|
114
|
+
process.start_command = "/usr/bin/some_start_command"
|
115
|
+
process.pid_file = "/tmp/some_pid_file.pid"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
108
119
|
To check for flapping:
|
109
120
|
|
110
121
|
process.checks :flapping, :times => 2, :within => 30.seconds, :retry_in => 7.seconds
|
122
|
+
|
123
|
+
To set the working directory to _cd_ into when starting the command:
|
124
|
+
|
125
|
+
Bluepill.application("app_name") do |app|
|
126
|
+
app.process("process_name") do |process|
|
127
|
+
process.start_command = "/usr/bin/some_start_command"
|
128
|
+
process.pid_file = "/tmp/some_pid_file.pid"
|
129
|
+
process.working_dir = "/path/to/some_directory"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
You can also have an app-wide working directory:
|
134
|
+
|
135
|
+
|
136
|
+
Bluepill.application("app_name") do |app|
|
137
|
+
app.working_dir = "/path/to/some_directory"
|
138
|
+
app.process("process_name") do |process|
|
139
|
+
process.start_command = "/usr/bin/some_start_command"
|
140
|
+
process.pid_file = "/tmp/some_pid_file.pid"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
Note: We also set the PWD in the environment to the working dir you specify. This is useful for when the working dir is a symlink. Unicorn in particular will cd into the environment variable in PWD when it re-execs to deal with a change in the symlink.
|
145
|
+
|
111
146
|
|
112
147
|
And lastly, to monitor child processes:
|
113
148
|
|
@@ -131,7 +166,7 @@ While you can specify shell tricks like the following in the start_command of a
|
|
131
166
|
end
|
132
167
|
end
|
133
168
|
|
134
|
-
We recommend that you
|
169
|
+
We recommend that you _not_ do that and instead use the config options to capture output from your daemons. Like so:
|
135
170
|
|
136
171
|
Bluepill.application("app_name") do |app|
|
137
172
|
app.process("process_name") do |process|
|
@@ -144,6 +179,7 @@ We recommend that you not do that and instead use the DSL to capture output from
|
|
144
179
|
end
|
145
180
|
end
|
146
181
|
|
182
|
+
The main benefit of using the config options is that Bluepill will be able to monitor the correct process instead of just watching the shell that spawned your actual server.
|
147
183
|
|
148
184
|
### CLI
|
149
185
|
To start a bluepill process and load a config:
|
@@ -165,6 +201,15 @@ To view the log for a process or group:
|
|
165
201
|
To quit bluepill:
|
166
202
|
|
167
203
|
sudo bluepill quit
|
204
|
+
|
205
|
+
### Logging
|
206
|
+
By default, bluepill uses syslog local6 facility as described in the installation section. But if for any reason you don't want to use syslog, you can use a log file. You can do this by setting the :log\_file option in the config:
|
207
|
+
|
208
|
+
Bluepill.application("app_name", :log_file => "/path/to/bluepill.log") do |app|
|
209
|
+
# ...
|
210
|
+
end
|
211
|
+
|
212
|
+
Keep in mind that you still need to set up log rotation (described in the installation section) to keep the log file from growing huge.
|
168
213
|
|
169
214
|
## Contribute
|
170
215
|
Code: [http://github.com/arya/bluepill](http://github.com/arya/bluepill)
|
data/Rakefile
CHANGED
@@ -24,21 +24,6 @@ rescue LoadError
|
|
24
24
|
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
25
25
|
end
|
26
26
|
|
27
|
-
require 'spec/rake/spectask'
|
28
|
-
Spec::Rake::SpecTask.new(:spec) do |spec|
|
29
|
-
spec.libs << 'lib' << 'spec'
|
30
|
-
spec.spec_files = FileList['spec/**/*_spec.rb']
|
31
|
-
end
|
32
|
-
|
33
|
-
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
34
|
-
spec.libs << 'lib' << 'spec'
|
35
|
-
spec.pattern = 'spec/**/*_spec.rb'
|
36
|
-
spec.rcov = true
|
37
|
-
end
|
38
|
-
|
39
|
-
task :spec => :check_dependencies
|
40
|
-
|
41
|
-
task :default => :spec
|
42
27
|
|
43
28
|
require 'rake/rdoctask'
|
44
29
|
Rake::RDocTask.new do |rdoc|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.22
|
data/bin/bluepill
CHANGED
@@ -3,7 +3,14 @@ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
|
3
3
|
require 'optparse'
|
4
4
|
require 'bluepill'
|
5
5
|
|
6
|
-
#
|
6
|
+
# Check for root
|
7
|
+
unless ::Process.euid == 0
|
8
|
+
$stderr.puts "You must run bluepill as root."
|
9
|
+
exit(3)
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
# Default options
|
7
14
|
options = {
|
8
15
|
:log_file => "/var/log/bluepill.log",
|
9
16
|
:base_dir => "/var/bluepill"
|
@@ -11,14 +18,30 @@ options = {
|
|
11
18
|
|
12
19
|
OptionParser.new do |opts|
|
13
20
|
opts.banner = "Usage: bluepill [app] cmd [options]"
|
14
|
-
|
15
|
-
opts.on("--logfile LOGFILE") do |file|
|
21
|
+
opts.on('-l', "--logfile LOGFILE", "Path to logfile, defaults to #{options[:log_file]}") do |file|
|
16
22
|
options[:log_file] = file
|
17
23
|
end
|
18
24
|
|
19
|
-
opts.on("--base-dir DIR") do |base_dir|
|
25
|
+
opts.on('-c', "--base-dir DIR", "Directory to store bluepill socket and pid files, defaults to #{options[:base_dir]}") do |base_dir|
|
20
26
|
options[:base_dir] = base_dir
|
21
27
|
end
|
28
|
+
|
29
|
+
opts.on_tail('-h','--help', 'Show this message') do
|
30
|
+
puts opts
|
31
|
+
puts
|
32
|
+
puts "Commands:"
|
33
|
+
puts " load CONFIG_FILE\t\tLoads new instance of bluepill using the specified config file"
|
34
|
+
puts " status\t\t\tLists the status of the proceses for the specified app"
|
35
|
+
puts " start TARGET\t\tIssues the start command for the target process or group"
|
36
|
+
puts " stop TARGET\t\t\tIssues the stop command for the target process or group"
|
37
|
+
puts " restart TARGET\t\tIssues the restart command for the target process or group"
|
38
|
+
puts " unmonitor TARGET\t\tStop monitoring target process or group"
|
39
|
+
puts " log [TARGET]\t\tShow the log for the specified process or group, defaults to all for app"
|
40
|
+
puts " quit\t\t\tStop bluepill"
|
41
|
+
puts
|
42
|
+
puts "See http://github.com/arya/bluepill for README"
|
43
|
+
exit
|
44
|
+
end
|
22
45
|
end.parse!
|
23
46
|
|
24
47
|
APPLICATION_COMMANDS = %w(status start stop restart unmonitor quit log)
|
@@ -30,15 +53,18 @@ if controller.running_applications.include?(ARGV.first)
|
|
30
53
|
options[:application] = ARGV.shift
|
31
54
|
elsif APPLICATION_COMMANDS.include?(ARGV.first)
|
32
55
|
if controller.running_applications.length == 1
|
56
|
+
# there is only one, let's just use that
|
33
57
|
options[:application] = controller.running_applications.first
|
34
58
|
elsif controller.running_applications.length > 1
|
35
|
-
|
59
|
+
# There is more than one, tell them the list and exit
|
60
|
+
$stderr.puts "You must specify an application name to run that command. Here's the list of running applications:"
|
36
61
|
controller.running_applications.each_with_index do |app, index|
|
37
62
|
$stderr.puts " #{index + 1}. #{app}"
|
38
63
|
end
|
39
64
|
$stderr.puts "Usage: bluepill [app] cmd [options]"
|
40
65
|
exit(1)
|
41
66
|
else
|
67
|
+
# There are none running AND they aren't trying to start one
|
42
68
|
$stderr.puts "Error: There are no running bluepill daemons.\nTo start a bluepill daemon, use: bluepill load <config file>"
|
43
69
|
exit(2)
|
44
70
|
end
|
@@ -48,23 +74,23 @@ options[:command] = ARGV.shift
|
|
48
74
|
|
49
75
|
case options[:command]
|
50
76
|
when "load"
|
51
|
-
|
52
|
-
if File.exists?(
|
53
|
-
eval(File.read(
|
77
|
+
config_file = ARGV.shift
|
78
|
+
if File.exists?(config_file)
|
79
|
+
eval(File.read(config_file))
|
54
80
|
else
|
55
81
|
$stderr.puts "Can't find file: #{file}"
|
56
82
|
end
|
57
83
|
when "log"
|
58
|
-
|
59
|
-
|
84
|
+
requested_pattern = ARGV.shift
|
85
|
+
grep_pattern = controller.send_command_to_application(options[:application], :grep_pattern, requested_pattern)
|
60
86
|
|
61
|
-
|
62
|
-
puts "Tailing log for #{
|
63
|
-
Kernel.exec(
|
87
|
+
tail = "tail -n 100 -f #{options[:log_file]} | grep -E '#{grep_pattern}'"
|
88
|
+
puts "Tailing log for #{requested_pattern}..."
|
89
|
+
Kernel.exec(tail)
|
64
90
|
|
65
91
|
when *APPLICATION_COMMANDS
|
66
|
-
|
67
|
-
puts controller.
|
92
|
+
target = ARGV.shift
|
93
|
+
puts controller.send_command_to_application(options[:application], options[:command], target)
|
68
94
|
|
69
95
|
else
|
70
96
|
puts "Unknown command `%s` (or application `%s` has not been loaded yet)" % [options[:command], options[:command]]
|
data/bluepill.gemspec
CHANGED
@@ -1,29 +1,28 @@
|
|
1
1
|
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{bluepill}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.22"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Arya Asemanfar", "Gary Tsang", "Rohith Ravi"]
|
12
|
-
s.date = %q{2009-11-
|
12
|
+
s.date = %q{2009-11-22}
|
13
13
|
s.default_executable = %q{bluepill}
|
14
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
15
|
s.email = %q{entombedvirus@gmail.com}
|
16
16
|
s.executables = ["bluepill"]
|
17
17
|
s.extra_rdoc_files = [
|
18
18
|
"LICENSE",
|
19
|
-
"README.
|
19
|
+
"README.md"
|
20
20
|
]
|
21
21
|
s.files = [
|
22
|
-
".
|
23
|
-
".
|
24
|
-
"DESIGN.markdown",
|
22
|
+
".gitignore",
|
23
|
+
"DESIGN.md",
|
25
24
|
"LICENSE",
|
26
|
-
"README.
|
25
|
+
"README.md",
|
27
26
|
"Rakefile",
|
28
27
|
"VERSION",
|
29
28
|
"bin/bluepill",
|
@@ -48,21 +47,13 @@ Gem::Specification.new do |s|
|
|
48
47
|
"lib/bluepill/trigger.rb",
|
49
48
|
"lib/bluepill/triggers/flapping.rb",
|
50
49
|
"lib/bluepill/util/rotational_array.rb",
|
51
|
-
"lib/example.rb"
|
52
|
-
"spec/blue-pill_spec.rb",
|
53
|
-
"spec/process_spec.rb",
|
54
|
-
"spec/spec_helper.rb"
|
50
|
+
"lib/example.rb"
|
55
51
|
]
|
56
52
|
s.homepage = %q{http://github.com/arya/bluepill}
|
57
53
|
s.rdoc_options = ["--charset=UTF-8"]
|
58
54
|
s.require_paths = ["lib"]
|
59
|
-
s.rubygems_version = %q{1.3.
|
55
|
+
s.rubygems_version = %q{1.3.4}
|
60
56
|
s.summary = %q{A process monitor written in Ruby with stability and minimalism in mind.}
|
61
|
-
s.test_files = [
|
62
|
-
"spec/blue-pill_spec.rb",
|
63
|
-
"spec/process_spec.rb",
|
64
|
-
"spec/spec_helper.rb"
|
65
|
-
]
|
66
57
|
|
67
58
|
if s.respond_to? :specification_version then
|
68
59
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
@@ -89,4 +80,3 @@ Gem::Specification.new do |s|
|
|
89
80
|
s.add_dependency(%q<activesupport>, [">= 2.3.4"])
|
90
81
|
end
|
91
82
|
end
|
92
|
-
|
data/lib/bluepill.rb
CHANGED
data/lib/bluepill/application.rb
CHANGED
@@ -8,7 +8,7 @@ module Bluepill
|
|
8
8
|
self.base_dir = options[:base_dir] ||= '/var/bluepill'
|
9
9
|
self.socket_timeout = options[:socket_timeout] ||= 10
|
10
10
|
|
11
|
-
self.logger = Bluepill::Logger.new.prefix_with(self.name)
|
11
|
+
self.logger = Bluepill::Logger.new(options.slice(:log_file)).prefix_with(self.name)
|
12
12
|
|
13
13
|
self.groups = Hash.new
|
14
14
|
|
@@ -21,8 +21,10 @@ module Bluepill
|
|
21
21
|
begin
|
22
22
|
start_server
|
23
23
|
rescue StandardError => e
|
24
|
-
|
25
|
-
e.
|
24
|
+
$stderr.puts "Failed to start bluepill:"
|
25
|
+
$stderr.puts "%s `%s`" % [e.class.name, e.message]
|
26
|
+
$stderr.puts e.backtrace
|
27
|
+
exit(5)
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
@@ -113,6 +115,7 @@ module Bluepill
|
|
113
115
|
def quit
|
114
116
|
if @server
|
115
117
|
::Process.kill("TERM", 0)
|
118
|
+
"Terminating bluepill[#{::Process.pid}]"
|
116
119
|
else
|
117
120
|
send_to_server("quit")
|
118
121
|
end
|
@@ -141,13 +144,14 @@ module Bluepill
|
|
141
144
|
buffer
|
142
145
|
end
|
143
146
|
|
144
|
-
private
|
147
|
+
private
|
145
148
|
|
146
|
-
def
|
147
|
-
|
149
|
+
def start_listener
|
150
|
+
@listener_thread.kill if @listener_thread
|
151
|
+
@listener_thread = Thread.new(self) do |app|
|
148
152
|
begin
|
149
153
|
loop do
|
150
|
-
client = socket.accept
|
154
|
+
client = app.socket.accept
|
151
155
|
cmd = client.readline.strip
|
152
156
|
response = app.send(*cmd.split(":"))
|
153
157
|
client.write(response)
|
@@ -160,8 +164,9 @@ private
|
|
160
164
|
end
|
161
165
|
end
|
162
166
|
|
163
|
-
def
|
164
|
-
|
167
|
+
def start_worker
|
168
|
+
@worker_thread.kill if @worker_thread
|
169
|
+
@worker_thread = Thread.new(self) do |app|
|
165
170
|
loop do
|
166
171
|
begin
|
167
172
|
job = app.work_queue.pop
|
@@ -197,6 +202,7 @@ private
|
|
197
202
|
end
|
198
203
|
|
199
204
|
Daemonize.daemonize
|
205
|
+
self.logger.reopen
|
200
206
|
|
201
207
|
@server = true
|
202
208
|
$0 = "bluepilld: #{self.name}"
|
@@ -209,8 +215,8 @@ private
|
|
209
215
|
self.groups.each {|_, group| group.boot! }
|
210
216
|
|
211
217
|
setup_signal_traps
|
212
|
-
|
213
|
-
|
218
|
+
start_listener
|
219
|
+
start_worker
|
214
220
|
run
|
215
221
|
end
|
216
222
|
|
@@ -228,11 +234,17 @@ private
|
|
228
234
|
def setup_signal_traps
|
229
235
|
terminator = lambda do
|
230
236
|
puts "Terminating..."
|
237
|
+
File.unlink(self.socket.path) if self.socket
|
238
|
+
File.unlink(self.pid_file) if File.exists?(self.pid_file)
|
231
239
|
::Kernel.exit
|
232
240
|
end
|
233
241
|
|
234
242
|
Signal.trap("TERM", &terminator)
|
235
243
|
Signal.trap("INT", &terminator)
|
244
|
+
|
245
|
+
Signal.trap("HUP") do
|
246
|
+
self.logger.reopen
|
247
|
+
end
|
236
248
|
end
|
237
249
|
|
238
250
|
def grep_pattern(query = nil)
|
data/lib/bluepill/controller.rb
CHANGED
@@ -11,24 +11,24 @@ module Bluepill
|
|
11
11
|
self.pids_dir = File.join(base_dir, 'pids')
|
12
12
|
self.applications = Hash.new
|
13
13
|
setup_dir_structure
|
14
|
-
|
14
|
+
cleanup_bluepill_directory
|
15
15
|
end
|
16
16
|
|
17
17
|
def running_applications
|
18
18
|
Dir[File.join(sockets_dir, "*.sock")].map{|x| File.basename(x, ".sock")}
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
21
|
+
def send_command_to_application(application, command, *args)
|
22
22
|
applications[application] ||= Application.new(application, {:base_dir => base_dir})
|
23
23
|
applications[application].send(command.to_sym, *args.compact)
|
24
24
|
end
|
25
25
|
|
26
26
|
private
|
27
27
|
|
28
|
-
def
|
28
|
+
def cleanup_bluepill_directory
|
29
29
|
self.running_applications.each do |app|
|
30
30
|
pid = pid_for(app)
|
31
|
-
if !pid || !
|
31
|
+
if !pid || !System.pid_alive?(pid)
|
32
32
|
pid_file = File.join(self.pids_dir, "#{app}.pid")
|
33
33
|
sock_file = File.join(self.sockets_dir, "#{app}.sock")
|
34
34
|
File.unlink(pid_file) if File.exists?(pid_file)
|
@@ -42,23 +42,10 @@ module Bluepill
|
|
42
42
|
File.exists?(pid_file) && File.read(pid_file).to_i
|
43
43
|
end
|
44
44
|
|
45
|
-
def alive?(pid)
|
46
|
-
begin
|
47
|
-
::Process.kill(0, pid)
|
48
|
-
true
|
49
|
-
rescue Errno::ESRCH
|
50
|
-
false
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
45
|
def setup_dir_structure
|
55
46
|
[@sockets_dir, @pids_dir].each do |dir|
|
56
47
|
FileUtils.mkdir_p(dir) unless File.exists?(dir)
|
57
48
|
end
|
58
|
-
|
59
|
-
rescue Errno::EACCES
|
60
|
-
$stderr.puts "Error: You don't have permissions to #{base_dir}\nYou should run bluepill as root."
|
61
|
-
exit(3)
|
62
49
|
end
|
63
50
|
end
|
64
51
|
end
|
data/lib/bluepill/dsl.rb
CHANGED
@@ -5,7 +5,8 @@ module Bluepill
|
|
5
5
|
|
6
6
|
process_proxy = Class.new do
|
7
7
|
attr_reader :attributes, :watches
|
8
|
-
def initialize
|
8
|
+
def initialize(process_name = nil)
|
9
|
+
@name = process_name
|
9
10
|
@attributes = {}
|
10
11
|
@watches = {}
|
11
12
|
end
|
@@ -24,6 +25,14 @@ module Bluepill
|
|
24
25
|
@watches[name] = options
|
25
26
|
end
|
26
27
|
|
28
|
+
def validate_child_process(child)
|
29
|
+
unless child.attributes.has_key?(:stop_command)
|
30
|
+
$stderr.puts "Config Error: Invalid child process monitor for #{@name}"
|
31
|
+
$stderr.puts "You must specify a stop command to monitor child processes."
|
32
|
+
exit(6)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
27
36
|
def monitor_children(&child_process_block)
|
28
37
|
child_proxy = self.class.new
|
29
38
|
|
@@ -33,6 +42,7 @@ module Bluepill
|
|
33
42
|
child_proxy.restart_grace_time = @attributes[:restart_grace_time]
|
34
43
|
|
35
44
|
child_process_block.call(child_proxy)
|
45
|
+
validate_child_process(child_proxy)
|
36
46
|
|
37
47
|
@attributes[:child_process_template] = child_proxy.to_process(nil)
|
38
48
|
# @attributes[:child_process_template].freeze
|
@@ -55,16 +65,45 @@ module Bluepill
|
|
55
65
|
app_proxy = Class.new do
|
56
66
|
@@app = app
|
57
67
|
@@process_proxy = process_proxy
|
68
|
+
@@process_names = Hash.new # becuase I don't want to require Set just for validations
|
69
|
+
attr_accessor :working_dir, :uid, :gid
|
70
|
+
|
71
|
+
def validate_process(process, process_name)
|
72
|
+
if @@process_names.key?(process_name)
|
73
|
+
$stderr.puts "Config Error: You have two entries for the process name '#{process_name}'"
|
74
|
+
exit(6)
|
75
|
+
else
|
76
|
+
@@process_names[process_name] = 0
|
77
|
+
end
|
78
|
+
|
79
|
+
[:start_command, :pid_file].each do |required_attr|
|
80
|
+
if !process.attributes.key?(required_attr)
|
81
|
+
$stderr.puts "Config Error: You must specify a #{required_attr} for '#{process_name}'"
|
82
|
+
exit(6)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
58
86
|
|
59
87
|
def process(process_name, &process_block)
|
60
|
-
process_proxy = @@process_proxy.new
|
88
|
+
process_proxy = @@process_proxy.new(process_name)
|
61
89
|
process_block.call(process_proxy)
|
90
|
+
set_app_wide_attributes(process_proxy)
|
91
|
+
|
92
|
+
validate_process(process_proxy, process_name)
|
62
93
|
|
63
94
|
group = process_proxy.attributes.delete(:group)
|
64
95
|
process = process_proxy.to_process(process_name)
|
65
96
|
|
66
97
|
@@app.add_process(process, group)
|
67
98
|
end
|
99
|
+
|
100
|
+
def set_app_wide_attributes(process_proxy)
|
101
|
+
[:working_dir, :uid, :gid].each do |attribute|
|
102
|
+
unless process_proxy.attributes.key?(attribute)
|
103
|
+
process_proxy.attributes[attribute] = self.send(attribute)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
68
107
|
end
|
69
108
|
|
70
109
|
yield(app_proxy.new)
|
data/lib/bluepill/logger.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
-
module Bluepill
|
1
|
+
module Bluepill
|
2
2
|
class Logger
|
3
3
|
LOG_METHODS = [:emerg, :alert, :crit, :err, :warning, :notice, :info, :debug]
|
4
4
|
|
5
5
|
def initialize(options = {})
|
6
|
-
@
|
6
|
+
@options = options
|
7
|
+
@logger = options[:logger] || self.create_logger
|
7
8
|
@prefix = options[:prefix]
|
8
9
|
@prefixes = {}
|
9
10
|
end
|
@@ -25,5 +26,32 @@ module Bluepill
|
|
25
26
|
@prefixes[prefix] ||= self.class.new(:logger => self, :prefix => prefix)
|
26
27
|
end
|
27
28
|
|
29
|
+
def reopen
|
30
|
+
if @logger.is_a?(self.class)
|
31
|
+
@logger.reopen
|
32
|
+
else
|
33
|
+
@logger = create_logger
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
def create_logger
|
39
|
+
if @options[:log_file]
|
40
|
+
LoggerAdapter.new(@options[:log_file])
|
41
|
+
else
|
42
|
+
Syslog.close if Syslog.opened? # need to explictly close it before reopening it
|
43
|
+
Syslog.open(@options[:identity] || 'bluepilld', Syslog::LOG_PID, Syslog::LOG_LOCAL6)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class LoggerAdapter < ::Logger
|
48
|
+
LOGGER_EQUIVALENTS =
|
49
|
+
{:debug => :debug, :err => :error, :warning => :warn, :info => :info, :emerg => :fatal, :alert => :warn, :crit => :fatal, :notice => :info}
|
50
|
+
|
51
|
+
LOG_METHODS.each do |method|
|
52
|
+
next if method == LOGGER_EQUIVALENTS[method]
|
53
|
+
alias_method method, LOGGER_EQUIVALENTS[method]
|
54
|
+
end
|
55
|
+
end
|
28
56
|
end
|
29
57
|
end
|
data/lib/bluepill/socket.rb
CHANGED
@@ -7,7 +7,6 @@ module Bluepill
|
|
7
7
|
def initialize(name, base_dir)
|
8
8
|
self.name = name
|
9
9
|
self.base_dir = base_dir
|
10
|
-
@isserver = false
|
11
10
|
end
|
12
11
|
|
13
12
|
def client
|
@@ -15,11 +14,10 @@ module Bluepill
|
|
15
14
|
end
|
16
15
|
|
17
16
|
def server
|
18
|
-
@isserver = true
|
19
17
|
begin
|
20
18
|
self.socket = UNIXServer.open(socket_name)
|
21
19
|
rescue Errno::EADDRINUSE
|
22
|
-
#if sock file has been created. test to see if there is a server
|
20
|
+
# if sock file has been created. test to see if there is a server
|
23
21
|
tmp_socket = UNIXSocket.open(socket_name) rescue nil
|
24
22
|
if tmp_socket.nil?
|
25
23
|
cleanup
|
@@ -31,7 +29,7 @@ module Bluepill
|
|
31
29
|
end
|
32
30
|
|
33
31
|
def cleanup
|
34
|
-
File.delete(socket_name)
|
32
|
+
File.delete(socket_name)
|
35
33
|
end
|
36
34
|
|
37
35
|
def socket_name
|
data/lib/example.rb
CHANGED
@@ -60,20 +60,21 @@ Bluepill.application(:sample_app) do |app|
|
|
60
60
|
# process.checks :always_true, :every => 10
|
61
61
|
end
|
62
62
|
end
|
63
|
-
|
63
|
+
app.uid = "arya"
|
64
64
|
1.times do |i|
|
65
65
|
app.process("group_process_#{i}") do |process|
|
66
|
-
process.uid = "
|
67
|
-
process.gid = "wheel"
|
66
|
+
process.uid = "root"
|
67
|
+
# process.gid = "wheel"
|
68
68
|
|
69
69
|
process.stderr = "/tmp/err.log"
|
70
70
|
process.stdout = "/tmp/err.log"
|
71
71
|
|
72
72
|
|
73
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'}
|
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.start_command = "sleep 10"
|
75
76
|
process.daemonize = true
|
76
|
-
process.pid_file = "/tmp/
|
77
|
+
process.pid_file = "/tmp/p_#{process.group}_#{i}.pid"
|
77
78
|
|
78
79
|
# process.checks :always_true, :every => 5
|
79
80
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bluepill
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.22
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arya Asemanfar
|
@@ -11,7 +11,7 @@ autorequire:
|
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
13
|
|
14
|
-
date: 2009-11-
|
14
|
+
date: 2009-11-22 00:00:00 -08:00
|
15
15
|
default_executable: bluepill
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
@@ -72,13 +72,12 @@ extensions: []
|
|
72
72
|
|
73
73
|
extra_rdoc_files:
|
74
74
|
- LICENSE
|
75
|
-
- README.
|
75
|
+
- README.md
|
76
76
|
files:
|
77
|
-
- .document
|
78
77
|
- .gitignore
|
79
|
-
- DESIGN.
|
78
|
+
- DESIGN.md
|
80
79
|
- LICENSE
|
81
|
-
- README.
|
80
|
+
- README.md
|
82
81
|
- Rakefile
|
83
82
|
- VERSION
|
84
83
|
- bin/bluepill
|
@@ -104,9 +103,6 @@ files:
|
|
104
103
|
- lib/bluepill/triggers/flapping.rb
|
105
104
|
- lib/bluepill/util/rotational_array.rb
|
106
105
|
- lib/example.rb
|
107
|
-
- spec/blue-pill_spec.rb
|
108
|
-
- spec/process_spec.rb
|
109
|
-
- spec/spec_helper.rb
|
110
106
|
has_rdoc: true
|
111
107
|
homepage: http://github.com/arya/bluepill
|
112
108
|
licenses: []
|
@@ -131,11 +127,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
127
|
requirements: []
|
132
128
|
|
133
129
|
rubyforge_project:
|
134
|
-
rubygems_version: 1.3.
|
130
|
+
rubygems_version: 1.3.4
|
135
131
|
signing_key:
|
136
132
|
specification_version: 3
|
137
133
|
summary: A process monitor written in Ruby with stability and minimalism in mind.
|
138
|
-
test_files:
|
139
|
-
|
140
|
-
- spec/process_spec.rb
|
141
|
-
- spec/spec_helper.rb
|
134
|
+
test_files: []
|
135
|
+
|
data/.document
DELETED
data/spec/blue-pill_spec.rb
DELETED
data/spec/process_spec.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe "Bluepill:Process" do
|
4
|
-
it "should raise exceptions unless properly initialized" do
|
5
|
-
lambda {
|
6
|
-
Bluepill::Process.new
|
7
|
-
}.should raise_error(ArgumentError)
|
8
|
-
end
|
9
|
-
|
10
|
-
it "should construct a valid object when properly initialized" do
|
11
|
-
lambda {
|
12
|
-
Bluepill::Process.new("test_process") do |p|
|
13
|
-
# The absolute minimum to construct a valid process
|
14
|
-
p.start_command = "/dev/null"
|
15
|
-
p.pid_file = "/var/run/test_process.pid"
|
16
|
-
end
|
17
|
-
}.should_not raise_error
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
describe "A Bluepill::Process object" do
|
23
|
-
before(:each) do
|
24
|
-
@process = Bluepill::Process.new("test_process") do |p|
|
25
|
-
p.start_command = "hai"
|
26
|
-
p.daemonize = true
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should be in the unmonitored state after construction" do
|
31
|
-
@process.should be_unmonitored
|
32
|
-
end
|
33
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require "rubygems"
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
4
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
5
|
-
|
6
|
-
require 'bluepill'
|
7
|
-
require 'spec'
|
8
|
-
require 'spec/autorun'
|
9
|
-
require "ruby-debug"
|
10
|
-
|
11
|
-
Spec::Runner.configure do |config|
|
12
|
-
|
13
|
-
end
|