mojombo-god 0.7.7 → 0.7.9

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,4 +1,23 @@
1
- == _._._ / TBD
1
+ == 0.7.10 /
2
+ * Bug Fixes
3
+ * setup logging *after* loading a given config file when daemonized.
4
+ enables logging to the 'God.log_file' specified in a config file. [github.com/jnewland]
5
+ * New Conditions
6
+ * FileMtime < PollCondition - trigger on file mtime durations [github.com/jwilkins]
7
+ * New Contacts
8
+ * Twitter - allow messages to twitter [github.com/jwilkins]
9
+ * Campfire - send messages to 37signals' Campfire [github.com/hellvinz]
10
+ * Minor Enhancements
11
+ * Add watch log_cmd that can be reopened with STDOUT instead of a log file [github.com/jberkel]
12
+ * Added webhook output support [Martyn Loughran]
13
+
14
+ == 0.7.9 / 2008-08-06
15
+ * Major Enhancements
16
+ * Use a psuedo-priority queue for more efficient driver loop [Darrell Kresge]
17
+ * Bug Fixes
18
+ * Fix file_writable? when using chroot [github.com/eric]
19
+
20
+ == 0.7.8 / 2008-07-09
2
21
  * Bug Fixes
3
22
  * Catch all Exceptions from HttpResponseCode condition [github.com/rliebling]
4
23
  * Don't error out if the process went away in SlashProcPoller [Kevin Clark]
data/Manifest.txt CHANGED
@@ -24,6 +24,7 @@ lib/god/conditions/complex.rb
24
24
  lib/god/conditions/cpu_usage.rb
25
25
  lib/god/conditions/degrading_lambda.rb
26
26
  lib/god/conditions/disk_usage.rb
27
+ lib/god/conditions/file_mtime.rb
27
28
  lib/god/conditions/flapping.rb
28
29
  lib/god/conditions/http_response_code.rb
29
30
  lib/god/conditions/lambda.rb
@@ -33,7 +34,11 @@ lib/god/conditions/process_running.rb
33
34
  lib/god/conditions/tries.rb
34
35
  lib/god/configurable.rb
35
36
  lib/god/contact.rb
37
+ lib/god/contacts/campfire.rb
36
38
  lib/god/contacts/email.rb
39
+ lib/god/contacts/jabber.rb
40
+ lib/god/contacts/twitter.rb
41
+ lib/god/contacts/webhook.rb
37
42
  lib/god/dependency_graph.rb
38
43
  lib/god/diagnostics.rb
39
44
  lib/god/driver.rb
@@ -82,6 +87,7 @@ test/configs/test.rb
82
87
  test/helper.rb
83
88
  test/suite.rb
84
89
  test/test_behavior.rb
90
+ test/test_campfire.rb
85
91
  test/test_condition.rb
86
92
  test/test_conditions_disk_usage.rb
87
93
  test/test_conditions_http_response_code.rb
@@ -90,6 +96,7 @@ test/test_conditions_tries.rb
90
96
  test/test_contact.rb
91
97
  test/test_dependency_graph.rb
92
98
  test/test_driver.rb
99
+ test/test_email.rb
93
100
  test/test_event_handler.rb
94
101
  test/test_god.rb
95
102
  test/test_handlers_kqueue_handler.rb
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'hoe'
3
3
 
4
- Hoe.new('god', '0.7.7') do |p|
4
+ Hoe.new('god', '0.7.9') do |p|
5
5
  p.rubyforge_name = 'god'
6
6
  p.author = 'Tom Preston-Werner'
7
7
  p.email = 'tom@rubyisawesome.com'
data/lib/god.rb CHANGED
@@ -49,13 +49,23 @@ require 'god/conditions/flapping'
49
49
  require 'god/conditions/http_response_code'
50
50
  require 'god/conditions/disk_usage'
51
51
  require 'god/conditions/complex'
52
+ require 'god/conditions/file_mtime'
52
53
 
53
54
  require 'god/contact'
54
55
  require 'god/contacts/email'
56
+ require 'god/contacts/webhook'
57
+ begin
58
+ require 'god/contacts/twitter'
59
+ rescue LoadError
60
+ end
55
61
  begin
56
62
  require 'god/contacts/jabber'
57
63
  rescue LoadError
58
64
  end
65
+ begin
66
+ require 'god/contacts/campfire'
67
+ rescue LoadError
68
+ end
59
69
 
60
70
  require 'god/socket'
61
71
  require 'god/driver'
@@ -138,7 +148,7 @@ class Module
138
148
  end
139
149
 
140
150
  module God
141
- VERSION = '0.7.7'
151
+ VERSION = '0.7.9'
142
152
 
143
153
  LOG_BUFFER_SIZE_DEFAULT = 100
144
154
  PID_FILE_DIRECTORY_DEFAULTS = ['/var/run/god', '~/.god/pids']
data/lib/god/cli/run.rb CHANGED
@@ -64,6 +64,7 @@ module God
64
64
 
65
65
  load_config @options[:config]
66
66
  end
67
+ setup_logging
67
68
  end
68
69
 
69
70
  def run_in_front
@@ -74,18 +75,6 @@ module God
74
75
  end
75
76
 
76
77
  default_run
77
-
78
- log_file = God.log_file
79
- log_file = File.expand_path(@options[:log]) if @options[:log]
80
- if log_file
81
- puts "Sending output to log file: #{log_file}"
82
-
83
- # reset file descriptors
84
- STDIN.reopen "/dev/null"
85
- STDOUT.reopen(log_file, "a")
86
- STDERR.reopen STDOUT
87
- STDOUT.sync = true
88
- end
89
78
  end
90
79
 
91
80
  def run_daemonized
@@ -96,14 +85,6 @@ module God
96
85
  begin
97
86
  require 'god'
98
87
 
99
- log_file = @options[:log] || God.log_file || "/dev/null"
100
-
101
- # reset file descriptors
102
- STDIN.reopen "/dev/null"
103
- STDOUT.reopen(log_file, "a")
104
- STDERR.reopen STDOUT
105
- STDOUT.sync = true
106
-
107
88
  # set pid if requested
108
89
  if @options[:pid] # and as deamon
109
90
  God.pid = @options[:pid]
@@ -142,6 +123,21 @@ module God
142
123
  exit
143
124
  end
144
125
 
126
+ def setup_logging
127
+ log_file = God.log_file
128
+ log_file = File.expand_path(@options[:log]) if @options[:log]
129
+ log_file = "/dev/null" if !log_file && @options[:daemonize]
130
+ if log_file
131
+ puts "Sending output to log file: #{log_file}" unless @options[:daemonize]
132
+
133
+ # reset file descriptors
134
+ STDIN.reopen "/dev/null"
135
+ STDOUT.reopen(log_file, "a")
136
+ STDERR.reopen STDOUT
137
+ STDOUT.sync = true
138
+ end
139
+ end
140
+
145
141
  def load_config(config)
146
142
  if File.directory? config
147
143
  files_loaded = false
@@ -0,0 +1,28 @@
1
+ module God
2
+ module Conditions
3
+
4
+ class FileMtime < PollCondition
5
+ attr_accessor :path, :max_age
6
+
7
+ def initialize
8
+ super
9
+ self.path = nil
10
+ self.max_age = nil
11
+ end
12
+
13
+ def valid?
14
+ valid = true
15
+ valid &= complain("Attribute 'path' must be specified", self) if self.path.nil?
16
+ valid &= complain("Attribute 'max_age' must be specified", self) if self.max_age.nil?
17
+ valid
18
+ end
19
+
20
+ def test
21
+ (Time.now - File.mtime(self.path)) > self.max_age
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+
28
+
@@ -0,0 +1,82 @@
1
+ # notify campfire using tinder http://tinder.rubyforge.org
2
+ #
3
+ # Example: set up a new campfire notifier
4
+ #
5
+ # Credentials
6
+ #
7
+ # God::Contacts::Campfire.server_settings = {
8
+ # :subdomain => "yoursubdomain",
9
+ # :user_name => "youruser",
10
+ # :room => "yourroom",
11
+ # :password => "yourpassword"
12
+ # }
13
+ #
14
+ # Register a new notifier
15
+ #
16
+ # God.contact(:campfire) do |c|
17
+ # c.name = 'campfire'
18
+ # end
19
+ #
20
+ # Define a transition for the process running event
21
+ #
22
+ # w.transition(:up, :start) do |on|
23
+ # on.condition(:process_running) do |c|
24
+ # c.running = true
25
+ # c.notify = 'campfire'
26
+ # end
27
+ # end
28
+
29
+ require 'tinder'
30
+
31
+ module God
32
+ module Contacts
33
+
34
+ class Campfire < Contact
35
+ class << self
36
+ attr_accessor :server_settings, :format
37
+ end
38
+
39
+ self.server_settings = {:subdomain => '',
40
+ :user_name => '',
41
+ :password => '',
42
+ :room => ''}
43
+
44
+ self.format = lambda do |message, host|
45
+ <<-EOF
46
+ #{host} - #{message}
47
+ EOF
48
+ end
49
+
50
+ def initialize
51
+ @room = nil
52
+ end
53
+
54
+ def notify(message, time, priority, category, host)
55
+ begin
56
+ body = Campfire.format.call(message,host)
57
+
58
+ room.speak body
59
+
60
+ self.info = "notified campfire: #{Campfire.server_settings[:subdomain]}"
61
+ rescue => e
62
+ applog(nil, :info, "failed to notify campfire: #{e.message}")
63
+ applog(nil, :debug, e.backtrace.join("\n"))
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def room
70
+ unless @room
71
+ applog(nil,:debug, "initializing campfire connection using credentials: #{Campfire.server_settings.inspect}")
72
+
73
+ campfire = Tinder::Campfire.new Campfire.server_settings[:subdomain]
74
+ campfire.login Campfire.server_settings[:user_name], Campfire.server_settings[:password]
75
+ @room = campfire.find_room_by_name(Campfire.server_settings[:room])
76
+ end
77
+ @room
78
+ end
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,65 @@
1
+ # To add Jabber notifications you must have xmpp4r gem installed.
2
+ # Configure your watches like this:
3
+ #
4
+ # God::Contacts::Jabber.settings = { :jabber_id => 'sender@example.com',
5
+ # :password => 'secret' }
6
+ # God.contact(:jabber) do |c|
7
+ # c.name = 'Tester'
8
+ # c.jabber_id = 'receiver@example.com'
9
+ # c.group = 'developers'
10
+ # end
11
+
12
+ module XMPP4R
13
+ require 'rubygems'
14
+ require 'xmpp4r'
15
+ include Jabber
16
+ end
17
+
18
+ module God
19
+ module Contacts
20
+ class Jabber < Contact
21
+ class << self
22
+ attr_accessor :settings, :format
23
+ end
24
+
25
+ self.format = lambda do |message, priority, category, host|
26
+ text = "Message: #{message}\n"
27
+ text += "Host: #{host}\n" if host
28
+ text += "Priority: #{priority}\n" if priority
29
+ text += "Category: #{category}\n" if category
30
+ return text
31
+ end
32
+
33
+ attr_accessor :jabber_id
34
+
35
+ def valid?
36
+ valid = true
37
+ end
38
+
39
+ def notify(message, time, priority, category, host)
40
+ begin
41
+ jabber_id = XMPP4R::JID::new "#{Jabber.settings[:jabber_id]}/God"
42
+ jabber_client = XMPP4R::Client::new jabber_id
43
+ jabber_client.connect
44
+ jabber_client.auth Jabber.settings[:password]
45
+
46
+ body = Jabber.format.call message, priority, category, host
47
+
48
+ message = XMPP4R::Message::new self.jabber_id, body
49
+ message.set_type :normal
50
+ message.set_id '1'
51
+ message.set_subject 'God'
52
+ jabber_client.send message
53
+
54
+ self.info = "sent jabber message to #{self.jabber_id}"
55
+ rescue => e
56
+ puts e.message
57
+ puts e.backtrace.join("\n")
58
+
59
+ self.info = "failed to send jabber message to #{self.jabber_id}: #{e.message}"
60
+ end
61
+ end
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,39 @@
1
+ # For Twitter updates you need the 'twitter' gem
2
+ # (gem install twitter)
3
+ #
4
+ # Configure your watches like this:
5
+ #
6
+ # God::Contacts::Twitter.settings = { :username => 'sender@example.com',
7
+ # :password => 'secret' }
8
+ # God.contact(:twitter) do |c|
9
+ # c.name = 'Tester'
10
+ # c.group = 'developers'
11
+ # end
12
+
13
+ require 'rubygems'
14
+ require 'twitter'
15
+
16
+ module God
17
+ module Contacts
18
+ class Twitter < Contact
19
+ class << self
20
+ attr_accessor :settings
21
+ end
22
+
23
+ def valid?
24
+ valid = true
25
+ end
26
+
27
+ def notify(message, time, priority, category, host)
28
+ begin
29
+ ::Twitter::Base.new(Twitter.settings[:username],
30
+ Twitter.settings[:password]).update(message)
31
+
32
+ self.info = "sent twitter update as #{Twitter.settings[:username]}"
33
+ rescue => e
34
+ self.info = "failed to send twitter update from #{self.twitter_id}: #{e.message}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,47 @@
1
+ # Configure your watches like this:
2
+ #
3
+ # God.contact(:webhook) do |c|
4
+ # c.name = 'Tester'
5
+ # c.hook_url = 'http://hook/url'
6
+ # end
7
+
8
+ require 'net/http'
9
+ require 'uri'
10
+
11
+ module God
12
+ module Contacts
13
+
14
+ class Webhook < Contact
15
+
16
+ attr_accessor :hook_url
17
+
18
+ def valid?
19
+ valid = true
20
+ end
21
+
22
+ def notify(message, time, priority, category, host)
23
+ begin
24
+ data = {
25
+ :message => message,
26
+ :time => time,
27
+ :priority => priority,
28
+ :category => category,
29
+ :host => host
30
+ }
31
+
32
+ uri = URI.parse(self.hook_url)
33
+ Net::HTTP.post_form(uri, data)
34
+
35
+ self.info = "sent webhook to #{self.hook_url}"
36
+ rescue => e
37
+ puts e.message
38
+ puts e.backtrace.join("\n")
39
+
40
+ self.info = "failed to send webhook to #{self.hook_url}: #{e.message}"
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
data/lib/god/driver.rb CHANGED
@@ -1,47 +1,171 @@
1
1
  module God
2
-
3
- class DriverEvent
4
- attr_accessor :condition, :at
2
+ class TimedEvent
3
+ include Comparable
4
+
5
+ attr_accessor :at
5
6
 
6
- # Instantiate a new TimerEvent that will be triggered after the specified delay
7
- # +condition+ is the Condition
7
+ # Instantiate a new TimedEvent that will be triggered after the specified delay
8
8
  # +delay+ is the number of seconds from now at which to trigger
9
9
  #
10
- # Returns TimerEvent
11
- def initialize(condition, delay)
12
- self.condition = condition
10
+ # Returns TimedEvent
11
+ def initialize(delay = 0)
13
12
  self.at = Time.now + delay
14
13
  end
15
-
14
+
16
15
  def due?
17
16
  Time.now >= self.at
18
17
  end
18
+
19
+ def <=>(other)
20
+ self.at <=> other.at
21
+ end
22
+ end # DriverEvent
23
+
24
+ class DriverEvent < TimedEvent
25
+ attr_accessor :condition, :task
26
+
27
+ def initialize(delay, task, condition)
28
+ super delay
29
+ self.task = task
30
+ self.condition = condition
31
+ end
32
+
33
+ def handle_event
34
+ @task.handle_poll(@condition)
35
+ end
19
36
  end # DriverEvent
37
+
38
+ class DriverOperation < TimedEvent
39
+ attr_accessor :task, :name, :args
40
+
41
+ def initialize(task, name, args)
42
+ super(0)
43
+ self.task = task
44
+ self.name = name
45
+ self.args = args
46
+ end
47
+
48
+ # Handle the next queued operation that was issued asynchronously
49
+ #
50
+ # Returns nothing
51
+ def handle_event
52
+ @task.send(@name, *@args)
53
+ end
54
+ end
20
55
 
56
+ class DriverEventQueue
57
+ def initialize
58
+ @shutdown = false
59
+ @waiting = []
60
+ @events = []
61
+ @waiting.taint
62
+ @events.taint
63
+ self.taint
64
+ end
65
+
66
+ #
67
+ # Wake any sleeping threads after setting the sentinel
68
+ #
69
+ def shutdown
70
+ @shutdown = true
71
+ begin
72
+ Thread.critical = true
73
+ @waiting.each do |t|
74
+ t.run
75
+ end
76
+ ensure
77
+ Thread.critical = false
78
+ end
79
+ end
80
+
81
+ #
82
+ # Sleep until the queue has something due
83
+ #
84
+ def pop
85
+ begin
86
+ while (Thread.critical = true; @events.empty? or !@events.first.due?)
87
+ @waiting.push Thread.current
88
+ if @events.empty?
89
+ raise ThreadError, "queue empty" if @shutdown
90
+ Thread.stop
91
+ else
92
+ Thread.critical = false
93
+ sleep @events.first.at - Time.now
94
+ Thread.critical = true
95
+ end
96
+ end
97
+ @events.shift
98
+ ensure
99
+ Thread.critical = false
100
+ end
101
+ end
102
+
103
+ alias shift pop
104
+ alias deq pop
105
+
106
+ #
107
+ # Add an event to the queue, wake any waiters if what we added needs to
108
+ # happen sooner than the next pending event
109
+ #
110
+ def push(event)
111
+ Thread.critical = true
112
+ @events << event
113
+ @events.sort!
114
+ begin
115
+ t = @waiting.shift if @events.first == event
116
+ t.wakeup if t
117
+ rescue ThreadError
118
+ retry
119
+ ensure
120
+ Thread.critical = false
121
+ end
122
+ begin
123
+ t.run if t
124
+ rescue ThreadError
125
+ end
126
+ end
127
+
128
+ alias << push
129
+ alias enq push
130
+
131
+ def empty?
132
+ @que.empty?
133
+ end
134
+
135
+ def clear
136
+ @events.clear
137
+ end
138
+
139
+ def length
140
+ @events.length
141
+ end
142
+
143
+ alias size length
144
+
145
+ def num_waiting
146
+ @waiting.size
147
+ end
148
+ end
149
+
150
+
21
151
  class Driver
22
152
  attr_reader :thread
23
153
 
24
- INTERVAL = 0.25
25
-
26
154
  # Instantiate a new Driver and start the scheduler loop to handle events
27
155
  # +task+ is the Task this Driver belongs to
28
156
  #
29
157
  # Returns Driver
30
158
  def initialize(task)
31
159
  @task = task
32
- @events = []
33
- @ops = Queue.new
160
+ @events = God::DriverEventQueue.new
34
161
 
35
162
  @thread = Thread.new do
36
163
  loop do
37
164
  begin
38
- if !@ops.empty?
39
- self.handle_op
40
- elsif !@events.empty?
41
- self.handle_event
42
- else
43
- sleep INTERVAL
44
- end
165
+ @events.pop.handle_event
166
+ rescue ThreadError => e
167
+ # queue is empty
168
+ break
45
169
  rescue Exception => e
46
170
  message = format("Unhandled exception in driver loop - (%s): %s\n%s",
47
171
  e.class, e.message, e.backtrace.join("\n"))
@@ -51,31 +175,8 @@ module God
51
175
  end
52
176
  end
53
177
 
54
- # Handle the next queued operation that was issued asynchronously
55
- #
56
- # Returns nothing
57
- def handle_op
58
- command = @ops.pop
59
- @task.send(command[0], *command[1])
60
- end
61
-
62
- # Handle the next event (poll condition) that is due
63
- #
64
- # Returns nothing
65
- def handle_event
66
- if @events.first.due?
67
- event = @events.shift
68
- @task.handle_poll(event.condition)
69
- end
70
-
71
- # don't sleep if there is a pending event and it is due
72
- unless @events.first && @events.first.due?
73
- sleep INTERVAL
74
- end
75
- end
76
-
77
178
  # Clear all events for this Driver
78
- #
179
+ #
79
180
  # Returns nothing
80
181
  def clear_events
81
182
  @events.clear
@@ -87,7 +188,7 @@ module God
87
188
  #
88
189
  # Returns nothing
89
190
  def message(name, args = [])
90
- @ops.push([name, args])
191
+ @events.push(DriverOperation.new(@task, name, args))
91
192
  end
92
193
 
93
194
  # Create and schedule a new DriverEvent
@@ -98,11 +199,8 @@ module God
98
199
  def schedule(condition, delay = condition.interval)
99
200
  applog(nil, :debug, "driver schedule #{condition} in #{delay} seconds")
100
201
 
101
- @events.concat([DriverEvent.new(condition, delay)])
102
-
103
- # sort events
104
- @events.sort! { |x, y| x.at <=> y.at }
202
+ @events.push(DriverEvent.new(delay, @task, condition))
105
203
  end
106
204
  end # Driver
107
205
 
108
- end # God
206
+ end # God
data/lib/god/process.rb CHANGED
@@ -2,7 +2,7 @@ module God
2
2
  class Process
3
3
  WRITES_PID = [:start, :restart]
4
4
 
5
- attr_accessor :name, :uid, :gid, :log, :start, :stop, :restart, :unix_socket, :chroot, :env
5
+ attr_accessor :name, :uid, :gid, :log, :log_cmd, :start, :stop, :restart, :unix_socket, :chroot, :env
6
6
 
7
7
  def initialize
8
8
  self.log = '/dev/null'
@@ -12,6 +12,7 @@ module God
12
12
  @user_log = false
13
13
  @pid = nil
14
14
  @unix_socket = nil
15
+ @log_cmd = nil
15
16
  end
16
17
 
17
18
  def alive?
@@ -28,10 +29,11 @@ module God
28
29
  gid_num = Etc.getgrnam(self.gid).gid if self.gid
29
30
 
30
31
  ::Dir.chroot(self.chroot) if self.chroot
32
+ ::Process.groups = [gid_num] if self.gid
31
33
  ::Process::Sys.setgid(gid_num) if self.gid
32
34
  ::Process::Sys.setuid(uid_num) if self.uid
33
35
 
34
- File.writable?(file) ? exit(0) : exit(1)
36
+ File.writable?(file_in_chroot(file)) ? exit(0) : exit(1)
35
37
  end
36
38
 
37
39
  wpid, status = ::Process.waitpid2(pid)
@@ -267,12 +269,17 @@ module God
267
269
 
268
270
  ::Dir.chroot(self.chroot) if self.chroot
269
271
  ::Process.setsid
272
+ ::Process.groups = [gid_num] if self.gid
270
273
  ::Process::Sys.setgid(gid_num) if self.gid
271
274
  ::Process::Sys.setuid(uid_num) if self.uid
272
275
  Dir.chdir "/"
273
276
  $0 = command
274
277
  STDIN.reopen "/dev/null"
275
- STDOUT.reopen file_in_chroot(self.log), "a"
278
+ if self.log_cmd
279
+ STDOUT.reopen IO.popen(self.log_cmd, "a")
280
+ else
281
+ STDOUT.reopen file_in_chroot(self.log), "a"
282
+ end
276
283
  STDERR.reopen STDOUT
277
284
 
278
285
  # close any other file descriptors
@@ -15,7 +15,7 @@ module God
15
15
  # Returns true if +RequiredPaths+ are readable.
16
16
  def self.usable?
17
17
  RequiredPaths.all? do |path|
18
- test(?r, path)
18
+ test(?r, path) && readable?(path)
19
19
  end
20
20
  end
21
21
 
@@ -57,6 +57,16 @@ module God
57
57
 
58
58
  private
59
59
 
60
+ # Some systems (CentOS?) have a /proc, but they can hang when trying to
61
+ # read from them. Try to use this sparingly as it is expensive.
62
+ def self.readable?(path)
63
+ begin
64
+ timeout(1) { File.read(path) }
65
+ rescue Timeout::Error
66
+ false
67
+ end
68
+ end
69
+
60
70
  # in seconds
61
71
  def uptime
62
72
  File.read(UptimePath).split[0].to_f
data/lib/god/watch.rb CHANGED
@@ -13,7 +13,7 @@ module God
13
13
  extend Forwardable
14
14
  def_delegators :@process, :name, :uid, :gid, :start, :stop, :restart,
15
15
  :name=, :uid=, :gid=, :start=, :stop=, :restart=,
16
- :pid_file, :pid_file=, :log, :log=, :alive?, :pid,
16
+ :pid_file, :pid_file=, :log, :log=, :log_cmd, :log_cmd=, :alive?, :pid,
17
17
  :unix_socket, :unix_socket=, :chroot, :chroot=, :env, :env=
18
18
  #
19
19
  def initialize
@@ -6,6 +6,13 @@ God::Contacts::Email.server_settings = {
6
6
  :domain => "powerset.com"
7
7
  }
8
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
+
9
16
  God.contact(:email) do |c|
10
17
  c.name = 'tom'
11
18
  c.email = 'tom@mojombo.com'
@@ -24,12 +31,15 @@ God.contact(:email) do |c|
24
31
  c.group = 'platform'
25
32
  end
26
33
 
34
+ God.contact(:twitter) do |c|
35
+ c.name = 'tom2'
36
+ c.group = 'developers'
37
+ end
38
+
27
39
  God.watch do |w|
28
40
  w.name = "contact"
29
41
  w.interval = 5.seconds
30
42
  w.start = "ruby " + File.join(File.dirname(__FILE__), *%w[simple_server.rb])
31
- w.uid = 'tom'
32
- w.gid = 'tom'
33
43
  w.log = "/Users/tom/contact.log"
34
44
 
35
45
  # determine the state on startup
@@ -55,7 +65,7 @@ God.watch do |w|
55
65
  # start if process is not running
56
66
  w.transition(:up, :start) do |on|
57
67
  on.condition(:process_exits) do |c|
58
- c.notify = {:contacts => ['tom', 'foobar'], :priority => 1, :category => 'product'}
68
+ c.notify = {:contacts => ['tom2', 'foobar'], :priority => 1, :category => 'product'}
59
69
  end
60
70
  end
61
71
 
@@ -0,0 +1,41 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+ require 'tinder'
3
+
4
+ class TestCampfire < Test::Unit::TestCase
5
+ def test_exists
6
+ God::Contacts::Campfire
7
+ end
8
+
9
+ # should notify
10
+ def test_campfire_delivery_method_for_notify
11
+ assert_nothing_raised do
12
+
13
+ room = mock()
14
+ room.expects(:speak).returns(nil)
15
+
16
+ g = God::Contacts::Campfire.new
17
+ God::Contacts::Campfire.format.expects(:call).with(:a,:e)
18
+ g.expects(:room).returns(room)
19
+ g.notify(:a, :b, :c, :d, :e)
20
+ assert_equal "notified campfire: ", g.info
21
+ end
22
+ end
23
+
24
+ # should not establish a new connection because the older is alive
25
+ def test_campfire_room_method
26
+ assert_nothing_raised do
27
+ room = mock()
28
+ g = God::Contacts::Campfire.new
29
+ g.instance_variable_set(:@room,room)
30
+ assert_equal g.send(:room), room
31
+ end
32
+ end
33
+
34
+ # should raise because the connections parameters have not been set
35
+ def test_campfire_delivery_method_for_notify_without_campfire_params
36
+ LOG.expects(:log).times(3) # 3 calls: 2 debug (credentials, backtrace) + 1 info (failed message)
37
+ g = God::Contacts::Campfire.new
38
+ g.notify(:a, :b, :c, :d, :e)
39
+ end
40
+
41
+ end
@@ -6,7 +6,7 @@ class TestConditionsProcessRunning < Test::Unit::TestCase
6
6
  c = Conditions::ProcessRunning.new
7
7
  c.running = r
8
8
 
9
- c.stubs(:watch).returns(stub(:pid => 123, :name => 'foo'))
9
+ c.stubs(:watch).returns(stub(:pid => 99999999, :name => 'foo'))
10
10
 
11
11
  # no_stdout do
12
12
  assert_equal !r, c.test
@@ -0,0 +1,45 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestEmail < Test::Unit::TestCase
4
+ def test_exists
5
+ God::Contacts::Email
6
+ end
7
+
8
+ def test_unknown_delivery_method_for_notify
9
+ assert_nothing_raised do
10
+ God::Contacts::Email.any_instance.expects(:notify_smtp).never
11
+ God::Contacts::Email.any_instance.expects(:notify_sendmail).never
12
+ God::Contacts::Email.delivery_method = :foo_protocol
13
+ LOG.expects(:log).times(2)
14
+
15
+ g = God::Contacts::Email.new
16
+ g.notify(:a, :b, :c, :d, :e)
17
+ assert_nil g.info
18
+ end
19
+ end
20
+
21
+ def test_smtp_delivery_method_for_notify
22
+ assert_nothing_raised do
23
+ God::Contacts::Email.any_instance.expects(:notify_sendmail).never
24
+ God::Contacts::Email.any_instance.expects(:notify_smtp).once.returns(nil)
25
+ God::Contacts::Email.delivery_method = :smtp
26
+ g = God::Contacts::Email.new
27
+ g.email = 'joe@example.com'
28
+ g.notify(:a, :b, :c, :d, :e)
29
+ assert_equal "sent email to joe@example.com", g.info
30
+ end
31
+ end
32
+
33
+ def test_sendmail_delivery_method_for_notify
34
+ assert_nothing_raised do
35
+ God::Contacts::Email.any_instance.expects(:notify_smtp).never
36
+ God::Contacts::Email.any_instance.expects(:notify_sendmail).once.returns(nil)
37
+ God::Contacts::Email.delivery_method = :sendmail
38
+ g = God::Contacts::Email.new
39
+ g.email = 'joe@example.com'
40
+ g.notify(:a, :b, :c, :d, :e)
41
+ assert_equal "sent email to joe@example.com", g.info
42
+ end
43
+ end
44
+
45
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mojombo-god
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.7
4
+ version: 0.7.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Preston-Werner
@@ -50,6 +50,7 @@ files:
50
50
  - lib/god/conditions/cpu_usage.rb
51
51
  - lib/god/conditions/degrading_lambda.rb
52
52
  - lib/god/conditions/disk_usage.rb
53
+ - lib/god/conditions/file_mtime.rb
53
54
  - lib/god/conditions/flapping.rb
54
55
  - lib/god/conditions/http_response_code.rb
55
56
  - lib/god/conditions/lambda.rb
@@ -59,7 +60,11 @@ files:
59
60
  - lib/god/conditions/tries.rb
60
61
  - lib/god/configurable.rb
61
62
  - lib/god/contact.rb
63
+ - lib/god/contacts/campfire.rb
62
64
  - lib/god/contacts/email.rb
65
+ - lib/god/contacts/jabber.rb
66
+ - lib/god/contacts/twitter.rb
67
+ - lib/god/contacts/webhook.rb
63
68
  - lib/god/dependency_graph.rb
64
69
  - lib/god/diagnostics.rb
65
70
  - lib/god/driver.rb
@@ -108,6 +113,7 @@ files:
108
113
  - test/helper.rb
109
114
  - test/suite.rb
110
115
  - test/test_behavior.rb
116
+ - test/test_campfire.rb
111
117
  - test/test_condition.rb
112
118
  - test/test_conditions_disk_usage.rb
113
119
  - test/test_conditions_http_response_code.rb
@@ -116,6 +122,7 @@ files:
116
122
  - test/test_contact.rb
117
123
  - test/test_dependency_graph.rb
118
124
  - test/test_driver.rb
125
+ - test/test_email.rb
119
126
  - test/test_event_handler.rb
120
127
  - test/test_god.rb
121
128
  - test/test_handlers_kqueue_handler.rb