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.
Files changed (143) hide show
  1. checksums.yaml +7 -0
  2. data/Announce.txt +135 -0
  3. data/Gemfile +9 -0
  4. data/History.txt +469 -0
  5. data/LICENSE +22 -0
  6. data/README.md +37 -0
  7. data/Rakefile +185 -0
  8. data/TODO.md +37 -0
  9. data/bin/mcproc +134 -0
  10. data/doc/intro.asciidoc +20 -0
  11. data/doc/mcproc.asciidoc +1592 -0
  12. data/ext/god/.gitignore +5 -0
  13. data/ext/god/extconf.rb +56 -0
  14. data/ext/god/kqueue_handler.c +133 -0
  15. data/ext/god/netlink_handler.c +182 -0
  16. data/lib/god.rb +780 -0
  17. data/lib/god/behavior.rb +52 -0
  18. data/lib/god/behaviors/clean_pid_file.rb +21 -0
  19. data/lib/god/behaviors/clean_unix_socket.rb +21 -0
  20. data/lib/god/behaviors/notify_when_flapping.rb +51 -0
  21. data/lib/god/cli/command.rb +268 -0
  22. data/lib/god/cli/run.rb +170 -0
  23. data/lib/god/cli/version.rb +23 -0
  24. data/lib/god/compat19.rb +33 -0
  25. data/lib/god/condition.rb +96 -0
  26. data/lib/god/conditions/always.rb +36 -0
  27. data/lib/god/conditions/complex.rb +86 -0
  28. data/lib/god/conditions/cpu_usage.rb +80 -0
  29. data/lib/god/conditions/degrading_lambda.rb +52 -0
  30. data/lib/god/conditions/disk_usage.rb +32 -0
  31. data/lib/god/conditions/file_mtime.rb +28 -0
  32. data/lib/god/conditions/file_touched.rb +44 -0
  33. data/lib/god/conditions/flapping.rb +128 -0
  34. data/lib/god/conditions/http_response_code.rb +184 -0
  35. data/lib/god/conditions/lambda.rb +25 -0
  36. data/lib/god/conditions/memory_usage.rb +82 -0
  37. data/lib/god/conditions/process_exits.rb +66 -0
  38. data/lib/god/conditions/process_running.rb +63 -0
  39. data/lib/god/conditions/socket_responding.rb +142 -0
  40. data/lib/god/conditions/tries.rb +44 -0
  41. data/lib/god/configurable.rb +57 -0
  42. data/lib/god/contact.rb +114 -0
  43. data/lib/god/contacts/airbrake.rb +44 -0
  44. data/lib/god/contacts/campfire.rb +121 -0
  45. data/lib/god/contacts/email.rb +130 -0
  46. data/lib/god/contacts/hipchat.rb +117 -0
  47. data/lib/god/contacts/jabber.rb +75 -0
  48. data/lib/god/contacts/prowl.rb +57 -0
  49. data/lib/god/contacts/scout.rb +55 -0
  50. data/lib/god/contacts/sensu.rb +59 -0
  51. data/lib/god/contacts/slack.rb +98 -0
  52. data/lib/god/contacts/statsd.rb +46 -0
  53. data/lib/god/contacts/twitter.rb +51 -0
  54. data/lib/god/contacts/webhook.rb +74 -0
  55. data/lib/god/driver.rb +238 -0
  56. data/lib/god/errors.rb +24 -0
  57. data/lib/god/event_handler.rb +112 -0
  58. data/lib/god/event_handlers/dummy_handler.rb +13 -0
  59. data/lib/god/event_handlers/kqueue_handler.rb +17 -0
  60. data/lib/god/event_handlers/netlink_handler.rb +13 -0
  61. data/lib/god/logger.rb +109 -0
  62. data/lib/god/metric.rb +87 -0
  63. data/lib/god/process.rb +381 -0
  64. data/lib/god/registry.rb +32 -0
  65. data/lib/god/simple_logger.rb +59 -0
  66. data/lib/god/socket.rb +113 -0
  67. data/lib/god/sugar.rb +62 -0
  68. data/lib/god/sys_logger.rb +45 -0
  69. data/lib/god/system/portable_poller.rb +42 -0
  70. data/lib/god/system/process.rb +50 -0
  71. data/lib/god/system/slash_proc_poller.rb +92 -0
  72. data/lib/god/task.rb +552 -0
  73. data/lib/god/timeline.rb +25 -0
  74. data/lib/god/trigger.rb +43 -0
  75. data/lib/god/watch.rb +340 -0
  76. data/mcproc.gemspec +192 -0
  77. data/test/configs/child_events/child_events.god +44 -0
  78. data/test/configs/child_events/simple_server.rb +3 -0
  79. data/test/configs/child_polls/child_polls.god +37 -0
  80. data/test/configs/child_polls/simple_server.rb +12 -0
  81. data/test/configs/complex/complex.god +59 -0
  82. data/test/configs/complex/simple_server.rb +3 -0
  83. data/test/configs/contact/contact.god +118 -0
  84. data/test/configs/contact/simple_server.rb +3 -0
  85. data/test/configs/daemon_events/daemon_events.god +37 -0
  86. data/test/configs/daemon_events/simple_server.rb +8 -0
  87. data/test/configs/daemon_events/simple_server_stop.rb +11 -0
  88. data/test/configs/daemon_polls/daemon_polls.god +17 -0
  89. data/test/configs/daemon_polls/simple_server.rb +6 -0
  90. data/test/configs/degrading_lambda/degrading_lambda.god +31 -0
  91. data/test/configs/degrading_lambda/tcp_server.rb +15 -0
  92. data/test/configs/keepalive/keepalive.god +9 -0
  93. data/test/configs/keepalive/keepalive.rb +12 -0
  94. data/test/configs/lifecycle/lifecycle.god +25 -0
  95. data/test/configs/matias/matias.god +50 -0
  96. data/test/configs/real.rb +59 -0
  97. data/test/configs/running_load/running_load.god +16 -0
  98. data/test/configs/stop_options/simple_server.rb +12 -0
  99. data/test/configs/stop_options/stop_options.god +39 -0
  100. data/test/configs/stress/simple_server.rb +3 -0
  101. data/test/configs/stress/stress.god +15 -0
  102. data/test/configs/task/logs/.placeholder +0 -0
  103. data/test/configs/task/task.god +26 -0
  104. data/test/configs/test.rb +61 -0
  105. data/test/configs/usr1_trapper.rb +10 -0
  106. data/test/helper.rb +172 -0
  107. data/test/suite.rb +6 -0
  108. data/test/test_airbrake.rb +14 -0
  109. data/test/test_behavior.rb +18 -0
  110. data/test/test_campfire.rb +22 -0
  111. data/test/test_condition.rb +52 -0
  112. data/test/test_conditions_disk_usage.rb +50 -0
  113. data/test/test_conditions_http_response_code.rb +109 -0
  114. data/test/test_conditions_process_running.rb +40 -0
  115. data/test/test_conditions_socket_responding.rb +176 -0
  116. data/test/test_conditions_tries.rb +67 -0
  117. data/test/test_contact.rb +109 -0
  118. data/test/test_driver.rb +26 -0
  119. data/test/test_email.rb +34 -0
  120. data/test/test_event_handler.rb +82 -0
  121. data/test/test_god.rb +710 -0
  122. data/test/test_god_system.rb +201 -0
  123. data/test/test_handlers_kqueue_handler.rb +16 -0
  124. data/test/test_hipchat.rb +23 -0
  125. data/test/test_jabber.rb +29 -0
  126. data/test/test_logger.rb +55 -0
  127. data/test/test_metric.rb +74 -0
  128. data/test/test_process.rb +263 -0
  129. data/test/test_prowl.rb +15 -0
  130. data/test/test_registry.rb +15 -0
  131. data/test/test_sensu.rb +11 -0
  132. data/test/test_slack.rb +57 -0
  133. data/test/test_socket.rb +34 -0
  134. data/test/test_statsd.rb +22 -0
  135. data/test/test_sugar.rb +42 -0
  136. data/test/test_system_portable_poller.rb +17 -0
  137. data/test/test_system_process.rb +30 -0
  138. data/test/test_task.rb +246 -0
  139. data/test/test_timeline.rb +37 -0
  140. data/test/test_trigger.rb +63 -0
  141. data/test/test_watch.rb +286 -0
  142. data/test/test_webhook.rb +22 -0
  143. metadata +475 -0
@@ -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
@@ -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