mcproc 2016.2.20
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 +7 -0
- data/Announce.txt +135 -0
- data/Gemfile +9 -0
- data/History.txt +469 -0
- data/LICENSE +22 -0
- data/README.md +37 -0
- data/Rakefile +185 -0
- data/TODO.md +37 -0
- data/bin/mcproc +134 -0
- data/doc/intro.asciidoc +20 -0
- data/doc/mcproc.asciidoc +1592 -0
- data/ext/god/.gitignore +5 -0
- data/ext/god/extconf.rb +56 -0
- data/ext/god/kqueue_handler.c +133 -0
- data/ext/god/netlink_handler.c +182 -0
- data/lib/god.rb +780 -0
- data/lib/god/behavior.rb +52 -0
- data/lib/god/behaviors/clean_pid_file.rb +21 -0
- data/lib/god/behaviors/clean_unix_socket.rb +21 -0
- data/lib/god/behaviors/notify_when_flapping.rb +51 -0
- data/lib/god/cli/command.rb +268 -0
- data/lib/god/cli/run.rb +170 -0
- data/lib/god/cli/version.rb +23 -0
- data/lib/god/compat19.rb +33 -0
- data/lib/god/condition.rb +96 -0
- data/lib/god/conditions/always.rb +36 -0
- data/lib/god/conditions/complex.rb +86 -0
- data/lib/god/conditions/cpu_usage.rb +80 -0
- data/lib/god/conditions/degrading_lambda.rb +52 -0
- data/lib/god/conditions/disk_usage.rb +32 -0
- data/lib/god/conditions/file_mtime.rb +28 -0
- data/lib/god/conditions/file_touched.rb +44 -0
- data/lib/god/conditions/flapping.rb +128 -0
- data/lib/god/conditions/http_response_code.rb +184 -0
- data/lib/god/conditions/lambda.rb +25 -0
- data/lib/god/conditions/memory_usage.rb +82 -0
- data/lib/god/conditions/process_exits.rb +66 -0
- data/lib/god/conditions/process_running.rb +63 -0
- data/lib/god/conditions/socket_responding.rb +142 -0
- data/lib/god/conditions/tries.rb +44 -0
- data/lib/god/configurable.rb +57 -0
- data/lib/god/contact.rb +114 -0
- data/lib/god/contacts/airbrake.rb +44 -0
- data/lib/god/contacts/campfire.rb +121 -0
- data/lib/god/contacts/email.rb +130 -0
- data/lib/god/contacts/hipchat.rb +117 -0
- data/lib/god/contacts/jabber.rb +75 -0
- data/lib/god/contacts/prowl.rb +57 -0
- data/lib/god/contacts/scout.rb +55 -0
- data/lib/god/contacts/sensu.rb +59 -0
- data/lib/god/contacts/slack.rb +98 -0
- data/lib/god/contacts/statsd.rb +46 -0
- data/lib/god/contacts/twitter.rb +51 -0
- data/lib/god/contacts/webhook.rb +74 -0
- data/lib/god/driver.rb +238 -0
- data/lib/god/errors.rb +24 -0
- data/lib/god/event_handler.rb +112 -0
- data/lib/god/event_handlers/dummy_handler.rb +13 -0
- data/lib/god/event_handlers/kqueue_handler.rb +17 -0
- data/lib/god/event_handlers/netlink_handler.rb +13 -0
- data/lib/god/logger.rb +109 -0
- data/lib/god/metric.rb +87 -0
- data/lib/god/process.rb +381 -0
- data/lib/god/registry.rb +32 -0
- data/lib/god/simple_logger.rb +59 -0
- data/lib/god/socket.rb +113 -0
- data/lib/god/sugar.rb +62 -0
- data/lib/god/sys_logger.rb +45 -0
- data/lib/god/system/portable_poller.rb +42 -0
- data/lib/god/system/process.rb +50 -0
- data/lib/god/system/slash_proc_poller.rb +92 -0
- data/lib/god/task.rb +552 -0
- data/lib/god/timeline.rb +25 -0
- data/lib/god/trigger.rb +43 -0
- data/lib/god/watch.rb +340 -0
- data/mcproc.gemspec +192 -0
- data/test/configs/child_events/child_events.god +44 -0
- data/test/configs/child_events/simple_server.rb +3 -0
- data/test/configs/child_polls/child_polls.god +37 -0
- data/test/configs/child_polls/simple_server.rb +12 -0
- data/test/configs/complex/complex.god +59 -0
- data/test/configs/complex/simple_server.rb +3 -0
- data/test/configs/contact/contact.god +118 -0
- data/test/configs/contact/simple_server.rb +3 -0
- data/test/configs/daemon_events/daemon_events.god +37 -0
- data/test/configs/daemon_events/simple_server.rb +8 -0
- data/test/configs/daemon_events/simple_server_stop.rb +11 -0
- data/test/configs/daemon_polls/daemon_polls.god +17 -0
- data/test/configs/daemon_polls/simple_server.rb +6 -0
- data/test/configs/degrading_lambda/degrading_lambda.god +31 -0
- data/test/configs/degrading_lambda/tcp_server.rb +15 -0
- data/test/configs/keepalive/keepalive.god +9 -0
- data/test/configs/keepalive/keepalive.rb +12 -0
- data/test/configs/lifecycle/lifecycle.god +25 -0
- data/test/configs/matias/matias.god +50 -0
- data/test/configs/real.rb +59 -0
- data/test/configs/running_load/running_load.god +16 -0
- data/test/configs/stop_options/simple_server.rb +12 -0
- data/test/configs/stop_options/stop_options.god +39 -0
- data/test/configs/stress/simple_server.rb +3 -0
- data/test/configs/stress/stress.god +15 -0
- data/test/configs/task/logs/.placeholder +0 -0
- data/test/configs/task/task.god +26 -0
- data/test/configs/test.rb +61 -0
- data/test/configs/usr1_trapper.rb +10 -0
- data/test/helper.rb +172 -0
- data/test/suite.rb +6 -0
- data/test/test_airbrake.rb +14 -0
- data/test/test_behavior.rb +18 -0
- data/test/test_campfire.rb +22 -0
- data/test/test_condition.rb +52 -0
- data/test/test_conditions_disk_usage.rb +50 -0
- data/test/test_conditions_http_response_code.rb +109 -0
- data/test/test_conditions_process_running.rb +40 -0
- data/test/test_conditions_socket_responding.rb +176 -0
- data/test/test_conditions_tries.rb +67 -0
- data/test/test_contact.rb +109 -0
- data/test/test_driver.rb +26 -0
- data/test/test_email.rb +34 -0
- data/test/test_event_handler.rb +82 -0
- data/test/test_god.rb +710 -0
- data/test/test_god_system.rb +201 -0
- data/test/test_handlers_kqueue_handler.rb +16 -0
- data/test/test_hipchat.rb +23 -0
- data/test/test_jabber.rb +29 -0
- data/test/test_logger.rb +55 -0
- data/test/test_metric.rb +74 -0
- data/test/test_process.rb +263 -0
- data/test/test_prowl.rb +15 -0
- data/test/test_registry.rb +15 -0
- data/test/test_sensu.rb +11 -0
- data/test/test_slack.rb +57 -0
- data/test/test_socket.rb +34 -0
- data/test/test_statsd.rb +22 -0
- data/test/test_sugar.rb +42 -0
- data/test/test_system_portable_poller.rb +17 -0
- data/test/test_system_process.rb +30 -0
- data/test/test_task.rb +246 -0
- data/test/test_timeline.rb +37 -0
- data/test/test_trigger.rb +63 -0
- data/test/test_watch.rb +286 -0
- data/test/test_webhook.rb +22 -0
- metadata +475 -0
data/lib/god/behavior.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module God
|
2
|
+
|
3
|
+
class Behavior
|
4
|
+
include Configurable
|
5
|
+
|
6
|
+
attr_accessor :watch
|
7
|
+
|
8
|
+
# Generate a Behavior of the given kind. The proper class is found by camel casing the
|
9
|
+
# kind (which is given as an underscored symbol).
|
10
|
+
# +kind+ is the underscored symbol representing the class (e.g. foo_bar for God::Behaviors::FooBar)
|
11
|
+
def self.generate(kind, watch)
|
12
|
+
sym = kind.to_s.capitalize.gsub(/_(.)/){$1.upcase}.intern
|
13
|
+
b = God::Behaviors.const_get(sym).new
|
14
|
+
b.watch = watch
|
15
|
+
b
|
16
|
+
rescue NameError
|
17
|
+
raise NoSuchBehaviorError.new("No Behavior found with the class name God::Behaviors::#{sym}")
|
18
|
+
end
|
19
|
+
|
20
|
+
def valid?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
#######
|
25
|
+
|
26
|
+
def before_start
|
27
|
+
end
|
28
|
+
|
29
|
+
def after_start
|
30
|
+
end
|
31
|
+
|
32
|
+
def before_restart
|
33
|
+
end
|
34
|
+
|
35
|
+
def after_restart
|
36
|
+
end
|
37
|
+
|
38
|
+
def before_stop
|
39
|
+
end
|
40
|
+
|
41
|
+
def after_stop
|
42
|
+
end
|
43
|
+
|
44
|
+
# Construct the friendly name of this Behavior, looks like:
|
45
|
+
#
|
46
|
+
# Behavior FooBar on Watch 'baz'
|
47
|
+
def friendly_name
|
48
|
+
"Behavior " + super + " on Watch '#{self.watch.name}'"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module God
|
2
|
+
module Behaviors
|
3
|
+
|
4
|
+
class CleanPidFile < Behavior
|
5
|
+
def valid?
|
6
|
+
valid = true
|
7
|
+
valid &= complain("Attribute 'pid_file' must be specified", self) if self.watch.pid_file.nil?
|
8
|
+
valid
|
9
|
+
end
|
10
|
+
|
11
|
+
def before_start
|
12
|
+
File.delete(self.watch.pid_file)
|
13
|
+
|
14
|
+
"deleted pid file"
|
15
|
+
rescue
|
16
|
+
"no pid file to delete"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module God
|
2
|
+
module Behaviors
|
3
|
+
|
4
|
+
class CleanUnixSocket < Behavior
|
5
|
+
def valid?
|
6
|
+
valid = true
|
7
|
+
valid &= complain("Attribute 'unix_socket' must be specified", self) if self.watch.unix_socket.nil?
|
8
|
+
valid
|
9
|
+
end
|
10
|
+
|
11
|
+
def before_start
|
12
|
+
File.delete(self.watch.unix_socket)
|
13
|
+
|
14
|
+
"deleted unix socket"
|
15
|
+
rescue
|
16
|
+
"no unix socket to delete"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module God
|
2
|
+
module Behaviors
|
3
|
+
|
4
|
+
class NotifyWhenFlapping < Behavior
|
5
|
+
attr_accessor :failures # number of failures
|
6
|
+
attr_accessor :seconds # number of seconds
|
7
|
+
attr_accessor :notifier # class to notify with
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
super
|
11
|
+
@startup_times = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid?
|
15
|
+
valid = true
|
16
|
+
valid &= complain("Attribute 'failures' must be specified", self) unless self.failures
|
17
|
+
valid &= complain("Attribute 'seconds' must be specified", self) unless self.seconds
|
18
|
+
valid &= complain("Attribute 'notifier' must be specified", self) unless self.notifier
|
19
|
+
|
20
|
+
# Must take one arg or variable args
|
21
|
+
unless self.notifier.respond_to?(:notify) and [1,-1].include?(self.notifier.method(:notify).arity)
|
22
|
+
valid &= complain("The 'notifier' must have a method 'notify' which takes 1 or variable args", self)
|
23
|
+
end
|
24
|
+
|
25
|
+
valid
|
26
|
+
end
|
27
|
+
|
28
|
+
def before_start
|
29
|
+
now = Time.now.to_i
|
30
|
+
@startup_times << now
|
31
|
+
check_for_flapping(now)
|
32
|
+
end
|
33
|
+
|
34
|
+
def before_restart
|
35
|
+
now = Time.now.to_i
|
36
|
+
@startup_times << now
|
37
|
+
check_for_flapping(now)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def check_for_flapping(now)
|
43
|
+
@startup_times.select! {|time| time >= now - self.seconds }
|
44
|
+
if @startup_times.length >= self.failures
|
45
|
+
self.notifier.notify("#{self.watch.name} has called start/restart #{@startup_times.length} times in #{self.seconds} seconds")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
module God
|
2
|
+
module CLI
|
3
|
+
|
4
|
+
class Command
|
5
|
+
def initialize(command, options, args)
|
6
|
+
@command = command
|
7
|
+
@options = options
|
8
|
+
@args = args
|
9
|
+
|
10
|
+
dispatch
|
11
|
+
end
|
12
|
+
|
13
|
+
def setup
|
14
|
+
# connect to drb unix socket
|
15
|
+
@server = DRbObject.new(nil, God::Socket.socket(@options[:port]))
|
16
|
+
|
17
|
+
# ping server to ensure that it is responsive
|
18
|
+
begin
|
19
|
+
@server.ping
|
20
|
+
rescue DRb::DRbConnError
|
21
|
+
puts "The server is not available (or you do not have permissions to access it)"
|
22
|
+
abort
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def dispatch
|
27
|
+
if %w{load status signal log quit terminate}.include?(@command)
|
28
|
+
setup
|
29
|
+
send("#{@command}_command")
|
30
|
+
elsif %w{start stop restart monitor unmonitor remove}.include?(@command)
|
31
|
+
setup
|
32
|
+
lifecycle_command
|
33
|
+
elsif @command == 'check'
|
34
|
+
check_command
|
35
|
+
else
|
36
|
+
puts "Command '#{@command}' is not valid. Run 'god --help' for usage"
|
37
|
+
abort
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def load_command
|
42
|
+
file = @args[1]
|
43
|
+
action = @args[2] || 'leave'
|
44
|
+
|
45
|
+
unless ['stop', 'remove', 'leave', ''].include?(action)
|
46
|
+
puts "Command '#{@command}' action must be either 'stop', 'remove' or 'leave'"
|
47
|
+
exit(1)
|
48
|
+
end
|
49
|
+
|
50
|
+
puts "Sending '#{@command}' command with action '#{action}'"
|
51
|
+
puts
|
52
|
+
|
53
|
+
unless File.exist?(file)
|
54
|
+
abort "File not found: #{file}"
|
55
|
+
end
|
56
|
+
|
57
|
+
affected, errors, removed = *@server.running_load(File.read(file), File.expand_path(file), action)
|
58
|
+
|
59
|
+
# output response
|
60
|
+
unless affected.empty?
|
61
|
+
puts 'The following tasks were affected:'
|
62
|
+
affected.each do |w|
|
63
|
+
puts ' ' + w
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
unless removed.empty?
|
68
|
+
puts 'The following tasks were removed:'
|
69
|
+
removed.each do |w|
|
70
|
+
puts ' ' + w
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
unless errors.empty?
|
75
|
+
puts errors
|
76
|
+
exit(1)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def status_command
|
81
|
+
exitcode = 0
|
82
|
+
statuses = @server.status
|
83
|
+
groups = {}
|
84
|
+
statuses.each do |name, status|
|
85
|
+
g = status[:group] || ''
|
86
|
+
groups[g] ||= {}
|
87
|
+
groups[g][name] = status
|
88
|
+
end
|
89
|
+
|
90
|
+
if item = @args[1]
|
91
|
+
if single = statuses[item]
|
92
|
+
# specified task (0 -> up, 1 -> unmonitored, 2 -> other)
|
93
|
+
state = single[:state]
|
94
|
+
puts "#{item}: #{state}"
|
95
|
+
exitcode = state == :up ? 0 : (state == :unmonitored ? 1 : 2)
|
96
|
+
elsif groups[item]
|
97
|
+
# specified group (0 -> up, N -> other)
|
98
|
+
puts "#{item}:"
|
99
|
+
groups[item].keys.sort.each do |name|
|
100
|
+
state = groups[item][name][:state]
|
101
|
+
print " "
|
102
|
+
puts "#{name}: #{state}"
|
103
|
+
exitcode += 1 unless state == :up
|
104
|
+
end
|
105
|
+
else
|
106
|
+
puts "Task or Group '#{item}' not found."
|
107
|
+
exit(1)
|
108
|
+
end
|
109
|
+
else
|
110
|
+
# show all groups and watches
|
111
|
+
groups.keys.sort.each do |group|
|
112
|
+
puts "#{group}:" unless group.empty?
|
113
|
+
groups[group].keys.sort.each do |name|
|
114
|
+
state = groups[group][name][:state]
|
115
|
+
print " " unless group.empty?
|
116
|
+
puts "#{name}: #{state}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
exit(exitcode)
|
122
|
+
end
|
123
|
+
|
124
|
+
def signal_command
|
125
|
+
# get the name of the watch/group
|
126
|
+
name = @args[1]
|
127
|
+
signal = @args[2]
|
128
|
+
|
129
|
+
puts "Sending signal '#{signal}' to '#{name}'"
|
130
|
+
|
131
|
+
t = Thread.new { loop { sleep(1); STDOUT.print('.'); STDOUT.flush; sleep(1) } }
|
132
|
+
|
133
|
+
watches = @server.signal(name, signal)
|
134
|
+
|
135
|
+
# output response
|
136
|
+
t.kill; STDOUT.puts
|
137
|
+
unless watches.empty?
|
138
|
+
puts 'The following watches were affected:'
|
139
|
+
watches.each do |w|
|
140
|
+
puts ' ' + w
|
141
|
+
end
|
142
|
+
else
|
143
|
+
puts 'No matching task or group'
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def log_command
|
148
|
+
begin
|
149
|
+
Signal.trap('INT') { exit }
|
150
|
+
name = @args[1]
|
151
|
+
|
152
|
+
unless name
|
153
|
+
puts "You must specify a Task or Group name"
|
154
|
+
exit!
|
155
|
+
end
|
156
|
+
|
157
|
+
puts "Please wait..."
|
158
|
+
t = Time.at(0)
|
159
|
+
loop do
|
160
|
+
print @server.running_log(name, t)
|
161
|
+
t = Time.now
|
162
|
+
sleep 0.25
|
163
|
+
end
|
164
|
+
rescue God::NoSuchWatchError
|
165
|
+
puts "No such watch"
|
166
|
+
rescue DRb::DRbConnError
|
167
|
+
puts "The server went away"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def quit_command
|
172
|
+
begin
|
173
|
+
@server.terminate
|
174
|
+
abort 'Could not stop god'
|
175
|
+
rescue DRb::DRbConnError
|
176
|
+
puts 'Stopped god'
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def terminate_command
|
181
|
+
t = Thread.new { loop { STDOUT.print('.'); STDOUT.flush; sleep(1) } }
|
182
|
+
if @server.stop_all
|
183
|
+
t.kill; STDOUT.puts
|
184
|
+
puts 'Stopped all watches'
|
185
|
+
else
|
186
|
+
t.kill; STDOUT.puts
|
187
|
+
puts "Could not stop all watches within #{@server.terminate_timeout} seconds"
|
188
|
+
end
|
189
|
+
|
190
|
+
begin
|
191
|
+
@server.terminate
|
192
|
+
abort 'Could not stop god'
|
193
|
+
rescue DRb::DRbConnError
|
194
|
+
puts 'Stopped god'
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def check_command
|
199
|
+
Thread.new do
|
200
|
+
begin
|
201
|
+
event_system = God::EventHandler.event_system
|
202
|
+
puts "using event system: #{event_system}"
|
203
|
+
|
204
|
+
if God::EventHandler.loaded?
|
205
|
+
puts "starting event handler"
|
206
|
+
God::EventHandler.start
|
207
|
+
else
|
208
|
+
puts "[fail] event system did not load"
|
209
|
+
exit(1)
|
210
|
+
end
|
211
|
+
|
212
|
+
puts 'forking off new process'
|
213
|
+
|
214
|
+
pid = fork do
|
215
|
+
loop { sleep(1) }
|
216
|
+
end
|
217
|
+
|
218
|
+
puts "forked process with pid = #{pid}"
|
219
|
+
|
220
|
+
God::EventHandler.register(pid, :proc_exit) do
|
221
|
+
puts "[ok] process exit event received"
|
222
|
+
exit!(0)
|
223
|
+
end
|
224
|
+
|
225
|
+
sleep(1)
|
226
|
+
|
227
|
+
puts "killing process"
|
228
|
+
|
229
|
+
::Process.kill('KILL', pid)
|
230
|
+
::Process.waitpid(pid)
|
231
|
+
rescue => e
|
232
|
+
puts e.message
|
233
|
+
puts e.backtrace.join("\n")
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
sleep(2)
|
238
|
+
|
239
|
+
puts "[fail] never received process exit event"
|
240
|
+
exit(1)
|
241
|
+
end
|
242
|
+
|
243
|
+
def lifecycle_command
|
244
|
+
# get the name of the watch/group
|
245
|
+
name = @args[1]
|
246
|
+
|
247
|
+
puts "Sending '#{@command}' command"
|
248
|
+
|
249
|
+
t = Thread.new { loop { sleep(1); STDOUT.print('.'); STDOUT.flush; sleep(1) } }
|
250
|
+
|
251
|
+
# send @command
|
252
|
+
watches = @server.control(name, @command)
|
253
|
+
|
254
|
+
# output response
|
255
|
+
t.kill; STDOUT.puts
|
256
|
+
unless watches.empty?
|
257
|
+
puts 'The following watches were affected:'
|
258
|
+
watches.each do |w|
|
259
|
+
puts ' ' + w
|
260
|
+
end
|
261
|
+
else
|
262
|
+
puts 'No matching task or group'
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end # Command
|
266
|
+
|
267
|
+
end
|
268
|
+
end
|
data/lib/god/cli/run.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
module God
|
2
|
+
module CLI
|
3
|
+
|
4
|
+
class Run
|
5
|
+
def initialize(options)
|
6
|
+
@options = options
|
7
|
+
|
8
|
+
dispatch
|
9
|
+
end
|
10
|
+
|
11
|
+
def dispatch
|
12
|
+
# have at_exit start god
|
13
|
+
$run = true
|
14
|
+
|
15
|
+
if @options[:syslog]
|
16
|
+
require 'god/sys_logger'
|
17
|
+
end
|
18
|
+
|
19
|
+
# run
|
20
|
+
if @options[:daemonize]
|
21
|
+
run_daemonized
|
22
|
+
else
|
23
|
+
run_in_front
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def attach
|
28
|
+
process = System::Process.new(@options[:attach])
|
29
|
+
Thread.new do
|
30
|
+
loop do
|
31
|
+
unless process.exists?
|
32
|
+
applog(nil, :info, "Going down because attached process #{@options[:attach]} exited")
|
33
|
+
exit!
|
34
|
+
end
|
35
|
+
sleep 5
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def default_run
|
41
|
+
# make sure we have STDIN/STDOUT redirected immediately
|
42
|
+
setup_logging
|
43
|
+
|
44
|
+
# start attached pid watcher if necessary
|
45
|
+
if @options[:attach]
|
46
|
+
self.attach
|
47
|
+
end
|
48
|
+
|
49
|
+
if @options[:port]
|
50
|
+
God.port = @options[:port]
|
51
|
+
end
|
52
|
+
|
53
|
+
if @options[:events]
|
54
|
+
God::EventHandler.load
|
55
|
+
end
|
56
|
+
|
57
|
+
# set log level, defaults to WARN
|
58
|
+
if @options[:log_level]
|
59
|
+
God.log_level = @options[:log_level]
|
60
|
+
else
|
61
|
+
God.log_level = @options[:daemonize] ? :warn : :info
|
62
|
+
end
|
63
|
+
|
64
|
+
if @options[:config]
|
65
|
+
if !@options[:config].include?('*') && !File.exist?(@options[:config])
|
66
|
+
abort "File not found: #{@options[:config]}"
|
67
|
+
end
|
68
|
+
|
69
|
+
# start the event handler
|
70
|
+
God::EventHandler.start if God::EventHandler.loaded?
|
71
|
+
|
72
|
+
load_config @options[:config]
|
73
|
+
end
|
74
|
+
setup_logging
|
75
|
+
end
|
76
|
+
|
77
|
+
def run_in_front
|
78
|
+
require 'god'
|
79
|
+
|
80
|
+
default_run
|
81
|
+
end
|
82
|
+
|
83
|
+
def run_daemonized
|
84
|
+
# trap and ignore SIGHUP
|
85
|
+
Signal.trap('HUP') {}
|
86
|
+
# trap and log-reopen SIGUSR1
|
87
|
+
Signal.trap('USR1') { setup_logging }
|
88
|
+
|
89
|
+
pid = fork do
|
90
|
+
begin
|
91
|
+
require 'god'
|
92
|
+
|
93
|
+
# set pid if requested
|
94
|
+
if @options[:pid] # and as deamon
|
95
|
+
God.pid = @options[:pid]
|
96
|
+
end
|
97
|
+
|
98
|
+
default_run
|
99
|
+
|
100
|
+
unless God::EventHandler.loaded?
|
101
|
+
puts
|
102
|
+
puts "***********************************************************************"
|
103
|
+
puts "*"
|
104
|
+
puts "* Event conditions are not available for your installation of god."
|
105
|
+
puts "* You may still use and write custom conditions using the poll system"
|
106
|
+
puts "*"
|
107
|
+
puts "***********************************************************************"
|
108
|
+
puts
|
109
|
+
end
|
110
|
+
|
111
|
+
rescue => e
|
112
|
+
puts e.message
|
113
|
+
puts e.backtrace.join("\n")
|
114
|
+
abort "There was a fatal system error while starting god (see above)"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
if @options[:pid]
|
119
|
+
File.open(@options[:pid], 'w') { |f| f.write pid }
|
120
|
+
end
|
121
|
+
|
122
|
+
::Process.detach pid
|
123
|
+
|
124
|
+
exit
|
125
|
+
end
|
126
|
+
|
127
|
+
def setup_logging
|
128
|
+
log_file = God.log_file
|
129
|
+
log_file = File.expand_path(@options[:log]) if @options[:log]
|
130
|
+
log_file = "/dev/null" if !log_file && @options[:daemonize]
|
131
|
+
if log_file
|
132
|
+
puts "Sending output to log file: #{log_file}" unless @options[:daemonize]
|
133
|
+
|
134
|
+
# reset file descriptors
|
135
|
+
STDIN.reopen "/dev/null"
|
136
|
+
STDOUT.reopen(log_file, "a")
|
137
|
+
STDERR.reopen STDOUT
|
138
|
+
STDOUT.sync = true
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def load_config(config)
|
143
|
+
files = File.directory?(config) ? Dir['**/*.god'] : Dir[config]
|
144
|
+
abort "No files could be found" if files.empty?
|
145
|
+
files.each do |god_file|
|
146
|
+
unless load_god_file(god_file)
|
147
|
+
abort "File '#{god_file}' could not be loaded"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def load_god_file(god_file)
|
153
|
+
applog(nil, :info, "Loading #{god_file}")
|
154
|
+
load File.expand_path(god_file)
|
155
|
+
true
|
156
|
+
rescue Exception => e
|
157
|
+
if e.instance_of?(SystemExit)
|
158
|
+
raise
|
159
|
+
else
|
160
|
+
puts "There was an error in #{god_file}"
|
161
|
+
puts "\t" + e.message
|
162
|
+
puts "\t" + e.backtrace.join("\n\t")
|
163
|
+
false
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end # Run
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|