god 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. data/Announce.txt +6 -6
  2. data/Gemfile +2 -0
  3. data/History.txt +19 -2
  4. data/{README.txt → LICENSE} +0 -37
  5. data/README.md +31 -0
  6. data/Rakefile +80 -38
  7. data/bin/god +21 -21
  8. data/doc/god.asciidoc +1487 -0
  9. data/doc/intro.asciidoc +20 -0
  10. data/ext/god/extconf.rb +3 -3
  11. data/ext/god/kqueue_handler.c +18 -18
  12. data/ext/god/netlink_handler.c +31 -31
  13. data/god.gemspec +24 -16
  14. data/lib/god.rb +261 -204
  15. data/lib/god/behavior.rb +14 -14
  16. data/lib/god/behaviors/clean_pid_file.rb +5 -5
  17. data/lib/god/behaviors/clean_unix_socket.rb +10 -10
  18. data/lib/god/behaviors/notify_when_flapping.rb +12 -12
  19. data/lib/god/cli/command.rb +59 -46
  20. data/lib/god/cli/run.rb +33 -37
  21. data/lib/god/cli/version.rb +6 -6
  22. data/lib/god/compat19.rb +1 -4
  23. data/lib/god/condition.rb +21 -21
  24. data/lib/god/conditions/always.rb +19 -6
  25. data/lib/god/conditions/complex.rb +18 -18
  26. data/lib/god/conditions/cpu_usage.rb +14 -14
  27. data/lib/god/conditions/degrading_lambda.rb +8 -8
  28. data/lib/god/conditions/disk_usage.rb +5 -5
  29. data/lib/god/conditions/flapping.rb +23 -23
  30. data/lib/god/conditions/http_response_code.rb +35 -19
  31. data/lib/god/conditions/lambda.rb +2 -2
  32. data/lib/god/conditions/memory_usage.rb +13 -13
  33. data/lib/god/conditions/process_exits.rb +14 -20
  34. data/lib/god/conditions/process_running.rb +16 -25
  35. data/lib/god/conditions/socket_responding.rb +132 -0
  36. data/lib/god/conditions/tries.rb +10 -10
  37. data/lib/god/configurable.rb +10 -10
  38. data/lib/god/contact.rb +20 -20
  39. data/lib/god/contacts/email.rb +7 -4
  40. data/lib/god/contacts/jabber.rb +1 -1
  41. data/lib/god/driver.rb +96 -64
  42. data/lib/god/errors.rb +9 -9
  43. data/lib/god/event_handler.rb +19 -19
  44. data/lib/god/event_handlers/dummy_handler.rb +4 -4
  45. data/lib/god/event_handlers/kqueue_handler.rb +3 -3
  46. data/lib/god/event_handlers/netlink_handler.rb +2 -2
  47. data/lib/god/logger.rb +13 -13
  48. data/lib/god/metric.rb +50 -22
  49. data/lib/god/process.rb +53 -52
  50. data/lib/god/registry.rb +7 -7
  51. data/lib/god/simple_logger.rb +14 -14
  52. data/lib/god/socket.rb +11 -11
  53. data/lib/god/sugar.rb +30 -15
  54. data/lib/god/sys_logger.rb +2 -2
  55. data/lib/god/system/portable_poller.rb +8 -8
  56. data/lib/god/system/process.rb +8 -8
  57. data/lib/god/system/slash_proc_poller.rb +13 -13
  58. data/lib/god/task.rb +237 -188
  59. data/lib/god/timeline.rb +5 -5
  60. data/lib/god/trigger.rb +11 -11
  61. data/lib/god/watch.rb +205 -53
  62. data/test/configs/child_events/child_events.god +5 -5
  63. data/test/configs/child_events/simple_server.rb +1 -1
  64. data/test/configs/child_polls/child_polls.god +4 -4
  65. data/test/configs/child_polls/simple_server.rb +4 -4
  66. data/test/configs/complex/complex.god +7 -7
  67. data/test/configs/complex/simple_server.rb +1 -1
  68. data/test/configs/contact/contact.god +1 -1
  69. data/test/configs/contact/simple_server.rb +1 -1
  70. data/test/configs/daemon_events/daemon_events.god +5 -5
  71. data/test/configs/daemon_events/simple_server.rb +1 -1
  72. data/test/configs/daemon_events/simple_server_stop.rb +1 -1
  73. data/test/configs/daemon_polls/daemon_polls.god +3 -3
  74. data/test/configs/daemon_polls/simple_server.rb +1 -1
  75. data/test/configs/degrading_lambda/degrading_lambda.god +3 -3
  76. data/test/configs/keepalive/keepalive.god +9 -0
  77. data/test/configs/keepalive/keepalive.rb +12 -0
  78. data/test/configs/lifecycle/lifecycle.god +2 -2
  79. data/test/configs/matias/matias.god +6 -6
  80. data/test/configs/real.rb +7 -7
  81. data/test/configs/running_load/running_load.god +2 -2
  82. data/test/configs/stop_options/simple_server.rb +1 -1
  83. data/test/configs/stress/simple_server.rb +1 -1
  84. data/test/configs/stress/stress.god +2 -2
  85. data/test/configs/task/task.god +5 -5
  86. data/test/configs/test.rb +7 -7
  87. data/test/helper.rb +8 -8
  88. data/test/test_behavior.rb +3 -3
  89. data/test/test_campfire.rb +1 -2
  90. data/test/test_condition.rb +10 -10
  91. data/test/test_conditions_disk_usage.rb +12 -12
  92. data/test/test_conditions_http_response_code.rb +24 -24
  93. data/test/test_conditions_process_running.rb +7 -7
  94. data/test/test_conditions_socket_responding.rb +122 -0
  95. data/test/test_conditions_tries.rb +12 -12
  96. data/test/test_contact.rb +19 -19
  97. data/test/test_driver.rb +17 -3
  98. data/test/test_event_handler.rb +12 -12
  99. data/test/test_god.rb +195 -117
  100. data/test/test_handlers_kqueue_handler.rb +4 -4
  101. data/test/test_jabber.rb +1 -1
  102. data/test/test_logger.rb +17 -17
  103. data/test/test_metric.rb +16 -16
  104. data/test/test_process.rb +47 -41
  105. data/test/test_prowl.rb +1 -1
  106. data/test/test_registry.rb +2 -2
  107. data/test/test_socket.rb +3 -3
  108. data/test/test_sugar.rb +7 -7
  109. data/test/test_system_portable_poller.rb +1 -1
  110. data/test/test_system_process.rb +5 -5
  111. data/test/test_task.rb +57 -57
  112. data/test/test_timeline.rb +8 -8
  113. data/test/test_trigger.rb +16 -16
  114. data/test/test_watch.rb +69 -62
  115. metadata +182 -69
  116. data/lib/god/dependency_graph.rb +0 -41
  117. data/lib/god/diagnostics.rb +0 -37
  118. data/test/test_dependency_graph.rb +0 -62
@@ -1,10 +1,10 @@
1
1
  module God
2
-
2
+
3
3
  class Behavior
4
4
  include Configurable
5
-
5
+
6
6
  attr_accessor :watch
7
-
7
+
8
8
  # Generate a Behavior of the given kind. The proper class is found by camel casing the
9
9
  # kind (which is given as an underscored symbol).
10
10
  # +kind+ is the underscored symbol representing the class (e.g. foo_bar for God::Behaviors::FooBar)
@@ -16,31 +16,31 @@ module God
16
16
  rescue NameError
17
17
  raise NoSuchBehaviorError.new("No Behavior found with the class name God::Behaviors::#{sym}")
18
18
  end
19
-
19
+
20
20
  def valid?
21
21
  true
22
22
  end
23
-
23
+
24
24
  #######
25
-
25
+
26
26
  def before_start
27
27
  end
28
-
28
+
29
29
  def after_start
30
30
  end
31
-
31
+
32
32
  def before_restart
33
33
  end
34
-
34
+
35
35
  def after_restart
36
36
  end
37
-
37
+
38
38
  def before_stop
39
39
  end
40
-
40
+
41
41
  def after_stop
42
42
  end
43
-
43
+
44
44
  # Construct the friendly name of this Behavior, looks like:
45
45
  #
46
46
  # Behavior FooBar on Watch 'baz'
@@ -48,5 +48,5 @@ module God
48
48
  "Behavior " + super + " on Watch '#{self.watch.name}'"
49
49
  end
50
50
  end
51
-
52
- end
51
+
52
+ end
@@ -1,21 +1,21 @@
1
1
  module God
2
2
  module Behaviors
3
-
3
+
4
4
  class CleanPidFile < Behavior
5
5
  def valid?
6
6
  valid = true
7
7
  valid &= complain("Attribute 'pid_file' must be specified", self) if self.watch.pid_file.nil?
8
8
  valid
9
9
  end
10
-
10
+
11
11
  def before_start
12
12
  File.delete(self.watch.pid_file)
13
-
13
+
14
14
  "deleted pid file"
15
15
  rescue
16
16
  "no pid file to delete"
17
17
  end
18
18
  end
19
-
19
+
20
20
  end
21
- end
21
+ end
@@ -1,21 +1,21 @@
1
1
  module God
2
2
  module Behaviors
3
-
3
+
4
4
  class CleanUnixSocket < Behavior
5
5
  def valid?
6
6
  valid = true
7
7
  valid &= complain("Attribute 'unix_socket' must be specified", self) if self.watch.unix_socket.nil?
8
8
  valid
9
- end
10
-
11
- def before_start
9
+ end
10
+
11
+ def before_start
12
12
  File.delete(self.watch.unix_socket)
13
-
14
- "deleted unix socket"
15
- rescue
16
- "no unix socket to delete"
13
+
14
+ "deleted unix socket"
15
+ rescue
16
+ "no unix socket to delete"
17
17
  end
18
18
  end
19
-
19
+
20
20
  end
21
- end
21
+ end
@@ -1,44 +1,44 @@
1
1
  module God
2
2
  module Behaviors
3
-
3
+
4
4
  class NotifyWhenFlapping < Behavior
5
- attr_accessor :failures # number of failures
5
+ attr_accessor :failures # number of failures
6
6
  attr_accessor :seconds # number of seconds
7
7
  attr_accessor :notifier # class to notify with
8
-
8
+
9
9
  def initialize
10
10
  super
11
11
  @startup_times = []
12
12
  end
13
-
13
+
14
14
  def valid?
15
15
  valid = true
16
16
  valid &= complain("Attribute 'failures' must be specified", self) unless self.failures
17
17
  valid &= complain("Attribute 'seconds' must be specified", self) unless self.seconds
18
18
  valid &= complain("Attribute 'notifier' must be specified", self) unless self.notifier
19
-
19
+
20
20
  # Must take one arg or variable args
21
21
  unless self.notifier.respond_to?(:notify) and [1,-1].include?(self.notifier.method(:notify).arity)
22
22
  valid &= complain("The 'notifier' must have a method 'notify' which takes 1 or variable args", self)
23
23
  end
24
-
24
+
25
25
  valid
26
26
  end
27
-
27
+
28
28
  def before_start
29
29
  now = Time.now.to_i
30
30
  @startup_times << now
31
31
  check_for_flapping(now)
32
32
  end
33
-
33
+
34
34
  def before_restart
35
35
  now = Time.now.to_i
36
36
  @startup_times << now
37
37
  check_for_flapping(now)
38
38
  end
39
-
39
+
40
40
  private
41
-
41
+
42
42
  def check_for_flapping(now)
43
43
  @startup_times.select! {|time| time >= now - self.seconds }
44
44
  if @startup_times.length >= self.failures
@@ -46,6 +46,6 @@ module God
46
46
  end
47
47
  end
48
48
  end
49
-
49
+
50
50
  end
51
- end
51
+ end
@@ -1,20 +1,20 @@
1
1
  module God
2
2
  module CLI
3
-
3
+
4
4
  class Command
5
5
  def initialize(command, options, args)
6
6
  @command = command
7
7
  @options = options
8
8
  @args = args
9
-
9
+
10
10
  dispatch
11
11
  end
12
-
12
+
13
13
  def setup
14
14
  # connect to drb unix socket
15
15
  DRb.start_service("druby://127.0.0.1:0")
16
16
  @server = DRbObject.new(nil, God::Socket.socket(@options[:port]))
17
-
17
+
18
18
  # ping server to ensure that it is responsive
19
19
  begin
20
20
  @server.ping
@@ -23,7 +23,7 @@ module God
23
23
  abort
24
24
  end
25
25
  end
26
-
26
+
27
27
  def dispatch
28
28
  if %w{load status signal log quit terminate}.include?(@command)
29
29
  setup
@@ -38,33 +38,46 @@ module God
38
38
  abort
39
39
  end
40
40
  end
41
-
41
+
42
42
  def load_command
43
43
  file = @args[1]
44
-
45
- puts "Sending '#{@command}' command"
44
+ action = @args[2] || 'leave'
45
+
46
+ unless ['stop', 'remove', 'leave', ''].include?(action)
47
+ puts "Command '#{@command}' action must be either 'stop', 'remove' or 'leave'"
48
+ exit(1)
49
+ end
50
+
51
+ puts "Sending '#{@command}' command with action '#{action}'"
46
52
  puts
47
-
53
+
48
54
  unless File.exist?(file)
49
55
  abort "File not found: #{file}"
50
56
  end
51
-
52
- names, errors = *@server.running_load(File.read(file), File.expand_path(file))
53
-
57
+
58
+ affected, errors, removed = *@server.running_load(File.read(file), File.expand_path(file), action)
59
+
54
60
  # output response
55
- unless names.empty?
61
+ unless affected.empty?
56
62
  puts 'The following tasks were affected:'
57
63
  names.each do |w|
58
64
  puts ' ' + w
59
65
  end
60
66
  end
61
-
67
+
68
+ unless removed.empty?
69
+ puts 'The following tasks were removed:'
70
+ removed.each do |w|
71
+ puts ' ' + w
72
+ end
73
+ end
74
+
62
75
  unless errors.empty?
63
76
  puts errors
64
77
  exit(1)
65
78
  end
66
79
  end
67
-
80
+
68
81
  def status_command
69
82
  exitcode = 0
70
83
  statuses = @server.status
@@ -74,13 +87,13 @@ module God
74
87
  groups[g] ||= {}
75
88
  groups[g][name] = status
76
89
  end
77
-
90
+
78
91
  if item = @args[1]
79
92
  if single = statuses[item]
80
93
  # specified task (0 -> up, 1 -> unmonitored, 2 -> other)
81
94
  state = single[:state]
82
95
  puts "#{item}: #{state}"
83
- exitcode = state == :up ? 0 : (state == :unmonitored ? 1 : 2)
96
+ exitcode = state == :up ? 0 : (state == :unmonitored ? 1 : 2)
84
97
  elsif groups[item]
85
98
  # specified group (0 -> up, N -> other)
86
99
  puts "#{item}:"
@@ -105,21 +118,21 @@ module God
105
118
  end
106
119
  end
107
120
  end
108
-
121
+
109
122
  exit(exitcode)
110
123
  end
111
-
124
+
112
125
  def signal_command
113
126
  # get the name of the watch/group
114
127
  name = @args[1]
115
128
  signal = @args[2]
116
-
129
+
117
130
  puts "Sending signal '#{signal}' to '#{name}'"
118
-
131
+
119
132
  t = Thread.new { loop { sleep(1); STDOUT.print('.'); STDOUT.flush; sleep(1) } }
120
-
133
+
121
134
  watches = @server.signal(name, signal)
122
-
135
+
123
136
  # output response
124
137
  t.kill; STDOUT.puts
125
138
  unless watches.empty?
@@ -131,17 +144,17 @@ module God
131
144
  puts 'No matching task or group'
132
145
  end
133
146
  end
134
-
147
+
135
148
  def log_command
136
149
  begin
137
150
  Signal.trap('INT') { exit }
138
151
  name = @args[1]
139
-
152
+
140
153
  unless name
141
154
  puts "You must specify a Task or Group name"
142
155
  exit!
143
156
  end
144
-
157
+
145
158
  puts "Please wait..."
146
159
  t = Time.at(0)
147
160
  loop do
@@ -155,7 +168,7 @@ module God
155
168
  puts "The server went away"
156
169
  end
157
170
  end
158
-
171
+
159
172
  def quit_command
160
173
  begin
161
174
  @server.terminate
@@ -164,7 +177,7 @@ module God
164
177
  puts 'Stopped god'
165
178
  end
166
179
  end
167
-
180
+
168
181
  def terminate_command
169
182
  t = Thread.new { loop { STDOUT.print('.'); STDOUT.flush; sleep(1) } }
170
183
  if @server.stop_all
@@ -174,7 +187,7 @@ module God
174
187
  t.kill; STDOUT.puts
175
188
  puts "Could not stop all watches within #{@server.terminate_timeout} seconds"
176
189
  end
177
-
190
+
178
191
  begin
179
192
  @server.terminate
180
193
  abort 'Could not stop god'
@@ -182,13 +195,13 @@ module God
182
195
  puts 'Stopped god'
183
196
  end
184
197
  end
185
-
198
+
186
199
  def check_command
187
200
  Thread.new do
188
201
  begin
189
202
  event_system = God::EventHandler.event_system
190
203
  puts "using event system: #{event_system}"
191
-
204
+
192
205
  if God::EventHandler.loaded?
193
206
  puts "starting event handler"
194
207
  God::EventHandler.start
@@ -196,24 +209,24 @@ module God
196
209
  puts "[fail] event system did not load"
197
210
  exit(1)
198
211
  end
199
-
212
+
200
213
  puts 'forking off new process'
201
-
214
+
202
215
  pid = fork do
203
216
  loop { sleep(1) }
204
217
  end
205
-
218
+
206
219
  puts "forked process with pid = #{pid}"
207
-
220
+
208
221
  God::EventHandler.register(pid, :proc_exit) do
209
222
  puts "[ok] process exit event received"
210
223
  exit!(0)
211
224
  end
212
-
225
+
213
226
  sleep(1)
214
-
227
+
215
228
  puts "killing process"
216
-
229
+
217
230
  ::Process.kill('KILL', pid)
218
231
  ::Process.waitpid(pid)
219
232
  rescue => e
@@ -221,24 +234,24 @@ module God
221
234
  puts e.backtrace.join("\n")
222
235
  end
223
236
  end
224
-
237
+
225
238
  sleep(2)
226
-
239
+
227
240
  puts "[fail] never received process exit event"
228
241
  exit(1)
229
242
  end
230
-
243
+
231
244
  def lifecycle_command
232
245
  # get the name of the watch/group
233
246
  name = @args[1]
234
-
247
+
235
248
  puts "Sending '#{@command}' command"
236
-
249
+
237
250
  t = Thread.new { loop { sleep(1); STDOUT.print('.'); STDOUT.flush; sleep(1) } }
238
-
251
+
239
252
  # send @command
240
253
  watches = @server.control(name, @command)
241
-
254
+
242
255
  # output response
243
256
  t.kill; STDOUT.puts
244
257
  unless watches.empty?
@@ -251,6 +264,6 @@ module God
251
264
  end
252
265
  end
253
266
  end # Command
254
-
267
+
255
268
  end
256
269
  end