samhendley-god 0.7.13

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 (115) hide show
  1. data/History.txt +293 -0
  2. data/Manifest.txt +114 -0
  3. data/README.txt +60 -0
  4. data/Rakefile +35 -0
  5. data/bin/god +128 -0
  6. data/examples/events.god +84 -0
  7. data/examples/gravatar.god +54 -0
  8. data/examples/single.god +66 -0
  9. data/ext/god/extconf.rb +55 -0
  10. data/ext/god/kqueue_handler.c +123 -0
  11. data/ext/god/netlink_handler.c +167 -0
  12. data/init/god +42 -0
  13. data/lib/god.rb +667 -0
  14. data/lib/god/behavior.rb +52 -0
  15. data/lib/god/behaviors/clean_pid_file.rb +21 -0
  16. data/lib/god/behaviors/clean_unix_socket.rb +21 -0
  17. data/lib/god/behaviors/notify_when_flapping.rb +51 -0
  18. data/lib/god/cli/command.rb +229 -0
  19. data/lib/god/cli/run.rb +176 -0
  20. data/lib/god/cli/version.rb +23 -0
  21. data/lib/god/condition.rb +96 -0
  22. data/lib/god/conditions/always.rb +23 -0
  23. data/lib/god/conditions/complex.rb +86 -0
  24. data/lib/god/conditions/cpu_usage.rb +80 -0
  25. data/lib/god/conditions/degrading_lambda.rb +52 -0
  26. data/lib/god/conditions/disk_usage.rb +27 -0
  27. data/lib/god/conditions/file_mtime.rb +28 -0
  28. data/lib/god/conditions/flapping.rb +128 -0
  29. data/lib/god/conditions/http_response_code.rb +168 -0
  30. data/lib/god/conditions/lambda.rb +25 -0
  31. data/lib/god/conditions/memory_usage.rb +82 -0
  32. data/lib/god/conditions/process_exits.rb +72 -0
  33. data/lib/god/conditions/process_running.rb +74 -0
  34. data/lib/god/conditions/tries.rb +44 -0
  35. data/lib/god/configurable.rb +57 -0
  36. data/lib/god/contact.rb +106 -0
  37. data/lib/god/contacts/campfire.rb +82 -0
  38. data/lib/god/contacts/email.rb +95 -0
  39. data/lib/god/contacts/jabber.rb +65 -0
  40. data/lib/god/contacts/twitter.rb +39 -0
  41. data/lib/god/contacts/webhook.rb +47 -0
  42. data/lib/god/dependency_graph.rb +41 -0
  43. data/lib/god/diagnostics.rb +37 -0
  44. data/lib/god/driver.rb +206 -0
  45. data/lib/god/errors.rb +24 -0
  46. data/lib/god/event_handler.rb +111 -0
  47. data/lib/god/event_handlers/dummy_handler.rb +13 -0
  48. data/lib/god/event_handlers/kqueue_handler.rb +17 -0
  49. data/lib/god/event_handlers/netlink_handler.rb +13 -0
  50. data/lib/god/logger.rb +120 -0
  51. data/lib/god/metric.rb +59 -0
  52. data/lib/god/process.rb +342 -0
  53. data/lib/god/registry.rb +32 -0
  54. data/lib/god/simple_logger.rb +53 -0
  55. data/lib/god/socket.rb +96 -0
  56. data/lib/god/sugar.rb +47 -0
  57. data/lib/god/system/portable_poller.rb +42 -0
  58. data/lib/god/system/process.rb +42 -0
  59. data/lib/god/system/slash_proc_poller.rb +92 -0
  60. data/lib/god/task.rb +491 -0
  61. data/lib/god/timeline.rb +25 -0
  62. data/lib/god/trigger.rb +43 -0
  63. data/lib/god/watch.rb +184 -0
  64. data/test/configs/child_events/child_events.god +44 -0
  65. data/test/configs/child_events/simple_server.rb +3 -0
  66. data/test/configs/child_polls/child_polls.god +37 -0
  67. data/test/configs/child_polls/simple_server.rb +12 -0
  68. data/test/configs/complex/complex.god +59 -0
  69. data/test/configs/complex/simple_server.rb +3 -0
  70. data/test/configs/contact/contact.god +84 -0
  71. data/test/configs/contact/simple_server.rb +3 -0
  72. data/test/configs/daemon_events/daemon_events.god +37 -0
  73. data/test/configs/daemon_events/simple_server.rb +8 -0
  74. data/test/configs/daemon_events/simple_server_stop.rb +11 -0
  75. data/test/configs/daemon_polls/daemon_polls.god +17 -0
  76. data/test/configs/daemon_polls/simple_server.rb +6 -0
  77. data/test/configs/degrading_lambda/degrading_lambda.god +31 -0
  78. data/test/configs/degrading_lambda/tcp_server.rb +15 -0
  79. data/test/configs/matias/matias.god +50 -0
  80. data/test/configs/real.rb +59 -0
  81. data/test/configs/running_load/running_load.god +16 -0
  82. data/test/configs/stress/simple_server.rb +3 -0
  83. data/test/configs/stress/stress.god +15 -0
  84. data/test/configs/task/logs/.placeholder +0 -0
  85. data/test/configs/task/task.god +26 -0
  86. data/test/configs/test.rb +61 -0
  87. data/test/helper.rb +151 -0
  88. data/test/suite.rb +6 -0
  89. data/test/test_behavior.rb +21 -0
  90. data/test/test_campfire.rb +41 -0
  91. data/test/test_condition.rb +50 -0
  92. data/test/test_conditions_disk_usage.rb +56 -0
  93. data/test/test_conditions_http_response_code.rb +109 -0
  94. data/test/test_conditions_process_running.rb +44 -0
  95. data/test/test_conditions_tries.rb +67 -0
  96. data/test/test_contact.rb +109 -0
  97. data/test/test_dependency_graph.rb +62 -0
  98. data/test/test_driver.rb +11 -0
  99. data/test/test_email.rb +45 -0
  100. data/test/test_event_handler.rb +80 -0
  101. data/test/test_god.rb +598 -0
  102. data/test/test_handlers_kqueue_handler.rb +16 -0
  103. data/test/test_logger.rb +63 -0
  104. data/test/test_metric.rb +72 -0
  105. data/test/test_process.rb +246 -0
  106. data/test/test_registry.rb +15 -0
  107. data/test/test_socket.rb +42 -0
  108. data/test/test_sugar.rb +42 -0
  109. data/test/test_system_portable_poller.rb +17 -0
  110. data/test/test_system_process.rb +30 -0
  111. data/test/test_task.rb +262 -0
  112. data/test/test_timeline.rb +37 -0
  113. data/test/test_trigger.rb +59 -0
  114. data/test/test_watch.rb +279 -0
  115. metadata +193 -0
@@ -0,0 +1,25 @@
1
+ module God
2
+
3
+ class Timeline < Array
4
+ # Instantiate a new Timeline
5
+ # +max_size+ is the maximum size to which the timeline should grow
6
+ #
7
+ # Returns Timeline
8
+ def initialize(max_size)
9
+ super()
10
+ @max_size = max_size
11
+ end
12
+
13
+ # Push a value onto the Timeline
14
+ # +val+ is the value to push
15
+ #
16
+ # Returns Timeline
17
+ def push(val)
18
+ self.concat([val])
19
+ shift if size > @max_size
20
+ end
21
+
22
+ alias_method :<<, :push
23
+ end
24
+
25
+ end
@@ -0,0 +1,43 @@
1
+ module God
2
+
3
+ class Trigger
4
+
5
+ class << self
6
+ attr_accessor :triggers # {task.name => condition}
7
+ end
8
+
9
+ # init
10
+ self.triggers = {}
11
+ @mutex = Mutex.new
12
+
13
+ def self.register(condition)
14
+ @mutex.synchronize do
15
+ self.triggers[condition.watch.name] ||= []
16
+ self.triggers[condition.watch.name] << condition
17
+ end
18
+ end
19
+
20
+ def self.deregister(condition)
21
+ @mutex.synchronize do
22
+ self.triggers[condition.watch.name].delete(condition)
23
+ self.triggers.delete(condition.watch.name) if self.triggers[condition.watch.name].empty?
24
+ end
25
+ end
26
+
27
+ def self.broadcast(task, message, payload)
28
+ return unless self.triggers[task.name]
29
+
30
+ @mutex.synchronize do
31
+ self.triggers[task.name].each do |t|
32
+ t.process(message, payload)
33
+ end
34
+ end
35
+ end
36
+
37
+ def self.reset
38
+ self.triggers.clear
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,184 @@
1
+ require 'etc'
2
+ require 'forwardable'
3
+
4
+ module God
5
+
6
+ class Watch < Task
7
+ VALID_STATES = [:init, :up, :start, :restart]
8
+ INITIAL_STATE = :init
9
+
10
+ # config
11
+ attr_accessor :grace, :start_grace, :stop_grace, :restart_grace
12
+
13
+ extend Forwardable
14
+ def_delegators :@process, :name, :uid, :gid, :start, :stop, :restart,
15
+ :name=, :uid=, :gid=, :start=, :stop=, :restart=,
16
+ :pid_file, :pid_file=, :log, :log=, :log_cmd, :log_cmd=, :alive?, :pid,
17
+ :unix_socket, :unix_socket=, :chroot, :chroot=, :env, :env=, :signal,
18
+ :dir, :dir=
19
+ #
20
+ def initialize
21
+ super
22
+
23
+ @process = God::Process.new
24
+
25
+ # valid states
26
+ self.valid_states = VALID_STATES
27
+ self.initial_state = INITIAL_STATE
28
+
29
+ # no grace period by default
30
+ self.grace = self.start_grace = self.stop_grace = self.restart_grace = 0
31
+ end
32
+
33
+ def valid?
34
+ super && @process.valid?
35
+ end
36
+
37
+ ###########################################################################
38
+ #
39
+ # Behavior
40
+ #
41
+ ###########################################################################
42
+
43
+ def behavior(kind)
44
+ # create the behavior
45
+ begin
46
+ b = Behavior.generate(kind, self)
47
+ rescue NoSuchBehaviorError => e
48
+ abort e.message
49
+ end
50
+
51
+ # send to block so config can set attributes
52
+ yield(b) if block_given?
53
+
54
+ # abort if the Behavior is invalid, the Behavior will have printed
55
+ # out its own error messages by now
56
+ abort unless b.valid?
57
+
58
+ self.behaviors << b
59
+ end
60
+
61
+ ###########################################################################
62
+ #
63
+ # Simple mode
64
+ #
65
+ ###########################################################################
66
+
67
+ def start_if
68
+ self.transition(:up, :start) do |on|
69
+ yield(on)
70
+ end
71
+ end
72
+
73
+ def restart_if
74
+ self.transition(:up, :restart) do |on|
75
+ yield(on)
76
+ end
77
+ end
78
+
79
+ def stop_if
80
+ self.transition(:up, :stop) do |on|
81
+ yield(on)
82
+ end
83
+ end
84
+
85
+ ###########################################################################
86
+ #
87
+ # Lifecycle
88
+ #
89
+ ###########################################################################
90
+
91
+ # Enable monitoring
92
+ def monitor
93
+ # start monitoring at the first available of the init or up states
94
+ if !self.metrics[:init].empty?
95
+ self.move(:init)
96
+ else
97
+ self.move(:up)
98
+ end
99
+ end
100
+
101
+ ###########################################################################
102
+ #
103
+ # Actions
104
+ #
105
+ ###########################################################################
106
+
107
+ def action(a, c = nil)
108
+ if Thread.current != self.driver.thread
109
+ # called from outside Driver
110
+
111
+ # send an async message to Driver
112
+ self.driver.message(:action, [a, c])
113
+ else
114
+ # called from within Driver
115
+
116
+ case a
117
+ when :start
118
+ call_action(c, :start)
119
+ sleep(self.start_grace + self.grace)
120
+ when :restart
121
+ if self.restart
122
+ call_action(c, :restart)
123
+ else
124
+ action(:stop, c)
125
+ action(:start, c)
126
+ end
127
+ sleep(self.restart_grace + self.grace)
128
+ when :stop
129
+ call_action(c, :stop)
130
+ sleep(self.stop_grace + self.grace)
131
+ end
132
+ end
133
+
134
+ self
135
+ end
136
+
137
+ def call_action(condition, action)
138
+ # before
139
+ before_items = self.behaviors
140
+ before_items += [condition] if condition
141
+ before_items.each do |b|
142
+ info = b.send("before_#{action}")
143
+ if info
144
+ msg = "#{self.name} before_#{action}: #{info} (#{b.base_name})"
145
+ applog(self, :info, msg)
146
+ end
147
+ end
148
+
149
+ # log
150
+ if self.send(action)
151
+ msg = "#{self.name} #{action}: #{self.send(action).to_s}"
152
+ applog(self, :info, msg)
153
+ end
154
+
155
+ @process.call_action(action)
156
+
157
+ # after
158
+ after_items = self.behaviors
159
+ after_items += [condition] if condition
160
+ after_items.each do |b|
161
+ info = b.send("after_#{action}")
162
+ if info
163
+ msg = "#{self.name} after_#{action}: #{info} (#{b.base_name})"
164
+ applog(self, :info, msg)
165
+ end
166
+ end
167
+ end
168
+
169
+ ###########################################################################
170
+ #
171
+ # Registration
172
+ #
173
+ ###########################################################################
174
+
175
+ def register!
176
+ God.registry.add(@process)
177
+ end
178
+
179
+ def unregister!
180
+ God.registry.remove(@process)
181
+ end
182
+ end
183
+
184
+ end
@@ -0,0 +1,44 @@
1
+ God.watch do |w|
2
+ w.name = "child-events"
3
+ w.interval = 5.seconds
4
+ w.start = File.join(GOD_ROOT, *%w[test configs child_events simple_server.rb])
5
+ # w.log = File.join(GOD_ROOT, *%w[test configs child_events god.log])
6
+
7
+ # determine the state on startup
8
+ w.transition(:init, { true => :up, false => :start }) do |on|
9
+ on.condition(:process_running) do |c|
10
+ c.running = true
11
+ end
12
+ end
13
+
14
+ # determine when process has finished starting
15
+ w.transition([:start, :restart], :up) do |on|
16
+ on.condition(:process_running) do |c|
17
+ c.running = true
18
+ end
19
+
20
+ # failsafe
21
+ on.condition(:tries) do |c|
22
+ c.times = 2
23
+ c.transition = :start
24
+ end
25
+ end
26
+
27
+ # start if process is not running
28
+ w.transition(:up, :start) do |on|
29
+ on.condition(:process_exits)
30
+ end
31
+
32
+ # lifecycle
33
+ w.lifecycle do |on|
34
+ on.condition(:flapping) do |c|
35
+ c.to_state = [:start, :restart]
36
+ c.times = 5
37
+ c.within = 20.seconds
38
+ c.transition = :unmonitored
39
+ c.retry_in = 10.seconds
40
+ c.retry_times = 2
41
+ c.retry_within = 5.minutes
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ loop { puts 'server'; sleep 1 }
@@ -0,0 +1,37 @@
1
+ God.watch do |w|
2
+ w.name = 'child-polls'
3
+ w.start = File.join(GOD_ROOT, *%w[test configs child_polls simple_server.rb])
4
+ w.interval = 5
5
+ w.grace = 2
6
+
7
+ w.start_if do |start|
8
+ start.condition(:process_running) do |c|
9
+ c.running = false
10
+ end
11
+ end
12
+
13
+ w.restart_if do |restart|
14
+ restart.condition(:cpu_usage) do |c|
15
+ c.above = 30.percent
16
+ c.times = [3, 5]
17
+ end
18
+
19
+ restart.condition(:memory_usage) do |c|
20
+ c.above = 10.megabytes
21
+ c.times = [3, 5]
22
+ end
23
+ end
24
+
25
+ # lifecycle
26
+ w.lifecycle do |on|
27
+ on.condition(:flapping) do |c|
28
+ c.to_state = [:start, :restart]
29
+ c.times = 3
30
+ c.within = 60.seconds
31
+ c.transition = :unmonitored
32
+ c.retry_in = 10.seconds
33
+ c.retry_times = 2
34
+ c.retry_within = 5.minutes
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,12 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ data = ''
4
+
5
+ loop do
6
+ STDOUT.puts('server');
7
+ STDOUT.flush;
8
+
9
+ 100000.times { data << 'x' }
10
+
11
+ sleep 10
12
+ end
@@ -0,0 +1,59 @@
1
+ God.watch do |w|
2
+ w.name = "complex"
3
+ w.interval = 5.seconds
4
+ w.start = File.join(GOD_ROOT, *%w[test configs complex simple_server.rb])
5
+ # w.log = File.join(GOD_ROOT, *%w[test configs child_events god.log])
6
+
7
+ # determine the state on startup
8
+ w.transition(:init, { true => :up, false => :start }) do |on|
9
+ on.condition(:process_running) do |c|
10
+ c.running = true
11
+ end
12
+ end
13
+
14
+ # determine when process has finished starting
15
+ w.transition([:start, :restart], :up) do |on|
16
+ on.condition(:process_running) do |c|
17
+ c.running = true
18
+ end
19
+
20
+ # failsafe
21
+ on.condition(:tries) do |c|
22
+ c.times = 2
23
+ c.transition = :start
24
+ end
25
+ end
26
+
27
+ # start if process is not running
28
+ w.transition(:up, :start) do |on|
29
+ on.condition(:process_exits)
30
+ end
31
+
32
+ # restart if process is misbehaving
33
+ w.transition(:up, :restart) do |on|
34
+ on.condition(:complex) do |cc|
35
+ cc.and(:cpu_usage) do |c|
36
+ c.above = 0.percent
37
+ c.times = 1
38
+ end
39
+
40
+ cc.and(:memory_usage) do |c|
41
+ c.above = 0.megabytes
42
+ c.times = 3
43
+ end
44
+ end
45
+ end
46
+
47
+ # lifecycle
48
+ w.lifecycle do |on|
49
+ on.condition(:flapping) do |c|
50
+ c.to_state = [:start, :restart]
51
+ c.times = 5
52
+ c.within = 20.seconds
53
+ c.transition = :unmonitored
54
+ c.retry_in = 10.seconds
55
+ c.retry_times = 2
56
+ c.retry_within = 5.minutes
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ loop { puts 'server'; sleep 1 }
@@ -0,0 +1,84 @@
1
+ God::Contacts::Email.message_settings = {:from => 'support@gravatar.com'}
2
+
3
+ God::Contacts::Email.server_settings = {
4
+ :address => "smtp.aa.powerset.com",
5
+ :port => 25,
6
+ :domain => "powerset.com"
7
+ }
8
+
9
+ God::Contacts::Twitter.settings = {
10
+ # this is for my 'mojombo2' twitter test account
11
+ # feel free to use it for testing your conditions
12
+ :username => 'mojombo@gmail.com',
13
+ :password => 'gok9we3ot1av2e'
14
+ }
15
+
16
+ God.contact(:email) do |c|
17
+ c.name = 'tom'
18
+ c.email = 'tom@mojombo.com'
19
+ c.group = 'developers'
20
+ end
21
+
22
+ God.contact(:email) do |c|
23
+ c.name = 'vanpelt'
24
+ c.email = 'vanpelt@example.com'
25
+ c.group = 'developers'
26
+ end
27
+
28
+ God.contact(:email) do |c|
29
+ c.name = 'kevin'
30
+ c.email = 'kevin@example.com'
31
+ c.group = 'platform'
32
+ end
33
+
34
+ God.contact(:twitter) do |c|
35
+ c.name = 'tom2'
36
+ c.group = 'developers'
37
+ end
38
+
39
+ God.watch do |w|
40
+ w.name = "contact"
41
+ w.interval = 5.seconds
42
+ w.start = "ruby " + File.join(File.dirname(__FILE__), *%w[simple_server.rb])
43
+ w.log = "/Users/tom/contact.log"
44
+
45
+ # determine the state on startup
46
+ w.transition(:init, { true => :up, false => :start }) do |on|
47
+ on.condition(:process_running) do |c|
48
+ c.running = true
49
+ end
50
+ end
51
+
52
+ # determine when process has finished starting
53
+ w.transition([:start, :restart], :up) do |on|
54
+ on.condition(:process_running) do |c|
55
+ c.running = true
56
+ end
57
+
58
+ # failsafe
59
+ on.condition(:tries) do |c|
60
+ c.times = 2
61
+ c.transition = :start
62
+ end
63
+ end
64
+
65
+ # start if process is not running
66
+ w.transition(:up, :start) do |on|
67
+ on.condition(:process_exits) do |c|
68
+ c.notify = {:contacts => ['tom2', 'foobar'], :priority => 1, :category => 'product'}
69
+ end
70
+ end
71
+
72
+ # lifecycle
73
+ w.lifecycle do |on|
74
+ on.condition(:flapping) do |c|
75
+ c.to_state = [:start, :restart]
76
+ c.times = 5
77
+ c.within = 20.seconds
78
+ c.transition = :unmonitored
79
+ c.retry_in = 10.seconds
80
+ c.retry_times = 2
81
+ c.retry_within = 5.minutes
82
+ end
83
+ end
84
+ end