god 0.10.1 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +9 -1
- data/god.gemspec +2 -3
- data/lib/god.rb +30 -22
- data/lib/god/contact.rb +8 -0
- data/lib/god/contacts/campfire.rb +97 -59
- data/lib/god/contacts/email.rb +80 -48
- data/lib/god/contacts/jabber.rb +50 -101
- data/lib/god/contacts/prowl.rb +25 -45
- data/lib/god/contacts/scout.rb +25 -34
- data/lib/god/contacts/twitter.rb +33 -20
- data/lib/god/contacts/webhook.rb +50 -24
- data/test/configs/contact/contact.god +59 -41
- data/test/test_campfire.rb +14 -32
- data/test/test_email.rb +22 -33
- data/test/test_jabber.rb +17 -24
- data/test/test_webhook.rb +7 -9
- metadata +37 -105
data/lib/god/contacts/jabber.rb
CHANGED
@@ -1,124 +1,73 @@
|
|
1
|
-
#
|
2
|
-
# Configure your watches like this:
|
1
|
+
# Send a notice to a Jabber address.
|
3
2
|
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# end
|
3
|
+
# host - The String hostname of the Jabber server.
|
4
|
+
# port - The Integer port of the Jabber server (default: 5222).
|
5
|
+
# from_jid - The String Jabber ID of the sender.
|
6
|
+
# password - The String password of the sender.
|
7
|
+
# to_jid - The String Jabber ID of the recipient.
|
8
|
+
# subject - The String subject of the message (default: "God Notification").
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
CONTACT_DEPS[:jabber] = ['xmpp4r']
|
11
|
+
CONTACT_DEPS[:jabber].each do |d|
|
12
|
+
require d
|
15
13
|
end
|
16
14
|
|
17
15
|
module God
|
18
16
|
module Contacts
|
19
|
-
|
17
|
+
|
18
|
+
class Jabber < Contact
|
20
19
|
class << self
|
21
|
-
attr_accessor :
|
20
|
+
attr_accessor :host, :port, :from_jid, :password, :to_jid, :subject
|
21
|
+
attr_accessor :format
|
22
22
|
end
|
23
|
-
|
24
|
-
self.
|
23
|
+
|
24
|
+
self.port = 5222
|
25
|
+
self.subject = 'God Notification'
|
26
|
+
|
27
|
+
self.format = lambda do |message, time, priority, category, host|
|
25
28
|
text = "Message: #{message}\n"
|
26
29
|
text += "Host: #{host}\n" if host
|
27
30
|
text += "Priority: #{priority}\n" if priority
|
28
31
|
text += "Category: #{category}\n" if category
|
29
|
-
|
32
|
+
text
|
30
33
|
end
|
31
|
-
|
32
|
-
attr_accessor :
|
33
|
-
|
34
|
+
|
35
|
+
attr_accessor :host, :port, :from_jid, :password, :to_jid, :subject
|
36
|
+
|
34
37
|
def valid?
|
35
38
|
valid = true
|
39
|
+
valid &= complain("Attribute 'host' must be specified", self) unless arg(:host)
|
40
|
+
valid &= complain("Attribute 'port' must be specified", self) unless arg(:port)
|
41
|
+
valid &= complain("Attribute 'from_jid' must be specified", self) unless arg(:from_jid)
|
42
|
+
valid &= complain("Attribute 'to_jid' must be specified", self) unless arg(:to_jid)
|
43
|
+
valid &= complain("Attribute 'password' must be specified", self) unless arg(:password)
|
44
|
+
valid
|
36
45
|
end
|
37
|
-
|
46
|
+
|
38
47
|
def notify(message, time, priority, category, host)
|
39
|
-
|
40
|
-
|
41
|
-
body = Jabber.format.call message, priority, category, host
|
42
|
-
|
43
|
-
message = XMPP4R::Message::new self.jabber_id, body
|
44
|
-
message.set_type :normal
|
45
|
-
message.set_id '1'
|
46
|
-
message.set_subject 'God'
|
47
|
-
|
48
|
-
self.send!(message)
|
49
|
-
|
50
|
-
self.info = "sent jabber message to #{self.jabber_id}"
|
51
|
-
rescue => e
|
52
|
-
puts e.message
|
53
|
-
puts e.backtrace.join("\n")
|
54
|
-
|
55
|
-
self.info = "failed to send jabber message to #{self.jabber_id}: #{e.message}"
|
56
|
-
end
|
57
|
-
|
58
|
-
def send!(msg)
|
59
|
-
attempts = 0
|
60
|
-
begin
|
61
|
-
attempts += 1
|
62
|
-
client.send(msg)
|
63
|
-
rescue Errno::EPIPE, IOError => e
|
64
|
-
sleep 1
|
65
|
-
disconnect!
|
66
|
-
reconnect!
|
67
|
-
retry unless attempts > 3
|
68
|
-
raise e
|
69
|
-
rescue Errno::ECONNRESET => e
|
70
|
-
sleep (attempts^2) * 60 + 60
|
71
|
-
disconnect!
|
72
|
-
reconnect!
|
73
|
-
retry unless attempts > 3
|
74
|
-
raise e
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def connect
|
79
|
-
connect! unless connected?
|
80
|
-
end
|
81
|
-
|
82
|
-
def connected?
|
83
|
-
connected = client.respond_to?(:is_connected?) && client.is_connected?
|
84
|
-
return connected
|
85
|
-
end
|
86
|
-
|
87
|
-
def connect!
|
88
|
-
disconnect! if connected?
|
48
|
+
body = Jabber.format.call(message, time, priority, category, host)
|
89
49
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
jabber_id = XMPP4R::JID::new "#{Jabber.settings[:jabber_id]}/God"
|
96
|
-
jabber_client = XMPP4R::Client::new jabber_id
|
97
|
-
jabber_client.connect Jabber.settings[:host]
|
98
|
-
jabber_client.auth Jabber.settings[:password]
|
99
|
-
self.client = jabber_client
|
100
|
-
|
101
|
-
@connect_mutex.unlock
|
102
|
-
end
|
103
|
-
|
104
|
-
def disconnect!
|
105
|
-
if client.respond_to?(:is_connected?) && client.is_connected?
|
106
|
-
begin
|
107
|
-
client.close
|
108
|
-
rescue Errno::EPIPE, IOError => e
|
109
|
-
self.info "Failed to disconnect: #{e}"
|
110
|
-
nil
|
111
|
-
end
|
112
|
-
end
|
113
|
-
client = nil
|
114
|
-
end
|
50
|
+
message = ::Jabber::Message.new(arg(:to_jid), body)
|
51
|
+
message.set_type(:normal)
|
52
|
+
message.set_id('1')
|
53
|
+
message.set_subject(arg(:subject))
|
115
54
|
|
116
|
-
|
117
|
-
Jabber.client
|
118
|
-
end
|
55
|
+
jabber_id = ::Jabber::JID.new("#{arg(:from_jid)}/God")
|
119
56
|
|
120
|
-
|
121
|
-
|
57
|
+
client = ::Jabber::Client.new(jabber_id)
|
58
|
+
client.connect(arg(:host), arg(:port))
|
59
|
+
client.auth(arg(:password))
|
60
|
+
client.send(message)
|
61
|
+
client.close
|
62
|
+
|
63
|
+
self.info = "sent jabber message to #{self.to_jid}"
|
64
|
+
rescue Object => e
|
65
|
+
if e.respond_to?(:message)
|
66
|
+
applog(nil, :info, "failed to send jabber message to #{arg(:to_jid)}: #{e.message}")
|
67
|
+
else
|
68
|
+
applog(nil, :info, "failed to send jabber message to #{arg(:to_jid)}: #{e.class}")
|
69
|
+
end
|
70
|
+
applog(nil, :debug, e.backtrace.join("\n"))
|
122
71
|
end
|
123
72
|
|
124
73
|
end
|
data/lib/god/contacts/prowl.rb
CHANGED
@@ -1,38 +1,19 @@
|
|
1
|
-
#
|
2
|
-
# (gem install prowly)
|
1
|
+
# Send a notice to Prowl (http://prowl.weks.net/).
|
3
2
|
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
# God.contact(:prowl) do |c|
|
7
|
-
# c.name = 'georgette'
|
8
|
-
# c.apikey = 'ffffffffffffffffffffffffffffffffffffffff'
|
9
|
-
# c.group = 'developers'
|
10
|
-
# end
|
11
|
-
#
|
12
|
-
#
|
13
|
-
# God.contact(:prowl) do |c|
|
14
|
-
# c.name = 'johnny'
|
15
|
-
# c.apikey = 'ffffffffffffffffffffffffffffffffffffffff'
|
16
|
-
# c.group = 'developers'
|
17
|
-
# end
|
18
|
-
#
|
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 = 'developers'
|
26
|
-
# end
|
27
|
-
# end
|
3
|
+
# apikey - The String API key.
|
28
4
|
|
29
|
-
|
5
|
+
CONTACT_DEPS[:prowl] = ['prowly']
|
6
|
+
CONTACT_DEPS[:prowl].each do |d|
|
7
|
+
require d
|
8
|
+
end
|
30
9
|
|
31
10
|
module God
|
32
11
|
module Contacts
|
33
12
|
class Prowl < Contact
|
34
13
|
|
35
|
-
|
14
|
+
class << self
|
15
|
+
attr_accessor :apikey
|
16
|
+
end
|
36
17
|
|
37
18
|
def valid?
|
38
19
|
valid = true
|
@@ -40,28 +21,27 @@ module God
|
|
40
21
|
valid
|
41
22
|
end
|
42
23
|
|
24
|
+
attr_accessor :apikey
|
25
|
+
|
43
26
|
def notify(message, time, priority, category, host)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
27
|
+
result = Prowly.notify do |n|
|
28
|
+
n.apikey = arg(:apikey)
|
29
|
+
n.priority = map_priority(priority.to_i)
|
30
|
+
n.application = category || "God"
|
31
|
+
n.event = "on " + host.to_s
|
32
|
+
n.description = message.to_s + " at " + time.to_s
|
33
|
+
end
|
52
34
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
35
|
+
if result.succeeded?
|
36
|
+
self.info = "sent prowl notification to #{self.name}"
|
37
|
+
else
|
38
|
+
self.info = "failed to send prowl notification to #{self.name}: #{result.message}"
|
58
39
|
end
|
59
40
|
rescue Object => e
|
60
|
-
|
41
|
+
applog(nil, :info, "failed to send prowl notification to #{self.name}: #{e.message}")
|
42
|
+
applog(nil, :debug, e.backtrace.join("\n"))
|
61
43
|
end
|
62
44
|
|
63
|
-
private
|
64
|
-
|
65
45
|
def map_priority(priority)
|
66
46
|
case priority
|
67
47
|
when 1 then Prowly::Notification::Priority::EMERGENCY
|
@@ -74,4 +54,4 @@ module God
|
|
74
54
|
end
|
75
55
|
end
|
76
56
|
end
|
77
|
-
end
|
57
|
+
end
|
data/lib/god/contacts/scout.rb
CHANGED
@@ -1,33 +1,19 @@
|
|
1
|
-
#
|
1
|
+
# Send a notice to Scout (http://scoutapp.com/).
|
2
2
|
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# A client key is configured per god process. Inside this God process,
|
6
|
-
# you can create multiple Scout 'contacts' - which are actually Scout
|
7
|
-
# plugins. This allows you to use Scout's UI to configure who gets
|
8
|
-
# notifications for each plugin, and to disable notifications when you
|
9
|
-
# go on vacation, etc.
|
10
|
-
#
|
11
|
-
# God.contact(:scout) do |c|
|
12
|
-
# c.name = 'scout_delayed_job_plugin'
|
13
|
-
# c.plugin_id = '12345
|
14
|
-
# end
|
15
|
-
#
|
16
|
-
# God.contact(:scout) do |c|
|
17
|
-
# c.name = 'scout_apache_plugin'
|
18
|
-
# c.plugin_id = '54312
|
19
|
-
# end
|
3
|
+
# client_key - The String client key.
|
4
|
+
# plugin_id - The String plugin id.
|
20
5
|
|
21
6
|
require 'net/http'
|
22
7
|
require 'uri'
|
23
8
|
|
24
9
|
module God
|
25
10
|
module Contacts
|
11
|
+
|
26
12
|
class Scout < Contact
|
27
13
|
class << self
|
28
|
-
attr_accessor :client_key, :
|
14
|
+
attr_accessor :client_key, :plugin_id
|
15
|
+
attr_accessor :format
|
29
16
|
end
|
30
|
-
attr_accessor :plugin_id
|
31
17
|
|
32
18
|
self.format = lambda do |message, priority, category, host|
|
33
19
|
text = "Message: #{message}\n"
|
@@ -37,28 +23,33 @@ module God
|
|
37
23
|
return text
|
38
24
|
end
|
39
25
|
|
26
|
+
attr_accessor :client_key, :plugin_id
|
27
|
+
|
40
28
|
def valid?
|
41
29
|
valid = true
|
30
|
+
valid &= complain("Attribute 'client_key' must be specified", self) unless arg(:client_key)
|
31
|
+
valid &= complain("Attribute 'plugin_id' must be specified", self) unless arg(:plugin_id)
|
32
|
+
valid
|
42
33
|
end
|
43
34
|
|
44
35
|
def notify(message, time, priority, category, host)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
}
|
36
|
+
data = {
|
37
|
+
:client_key => arg(:client_key),
|
38
|
+
:plugin_id => arg(:plugin_id),
|
39
|
+
:format => 'xml',
|
40
|
+
'alert[subject]' => message,
|
41
|
+
'alert[body]' => Scout.format.call(message, priority, category, host)
|
42
|
+
}
|
53
43
|
|
54
|
-
|
55
|
-
|
44
|
+
uri = URI.parse('http://scoutapp.com/alerts/create')
|
45
|
+
Net::HTTP.post_form(uri, data)
|
56
46
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
47
|
+
self.info = "sent scout alert to plugin ##{plugin_id}"
|
48
|
+
rescue => e
|
49
|
+
applog(nil, :info, "failed to send scout alert to plugin ##{plugin_id}: #{e.message}")
|
50
|
+
applog(nil, :debug, e.backtrace.join("\n"))
|
61
51
|
end
|
62
52
|
end
|
53
|
+
|
63
54
|
end
|
64
55
|
end
|
data/lib/god/contacts/twitter.rb
CHANGED
@@ -1,37 +1,50 @@
|
|
1
|
-
#
|
2
|
-
# (gem install twitter)
|
1
|
+
# Send a notice to a Twitter account (http://twitter.com/).
|
3
2
|
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# c.group = 'developers'
|
11
|
-
# end
|
3
|
+
# consumer_token - The String OAuth consumer token (defaults to God's
|
4
|
+
# existing consumer token).
|
5
|
+
# consumer_secret - The String OAuth consumer secret (defaults to God's
|
6
|
+
# existing consumer secret).
|
7
|
+
# access_token - The String OAuth access token.
|
8
|
+
# access_secret - The String OAuth access secret.
|
12
9
|
|
13
|
-
|
10
|
+
CONTACT_DEPS[:twitter] = ['twitter']
|
11
|
+
CONTACT_DEPS[:twitter].each do |d|
|
12
|
+
require d
|
13
|
+
end
|
14
14
|
|
15
15
|
module God
|
16
16
|
module Contacts
|
17
17
|
class Twitter < Contact
|
18
18
|
class << self
|
19
|
-
attr_accessor :
|
19
|
+
attr_accessor :consumer_token, :consumer_secret,
|
20
|
+
:access_token, :access_secret
|
20
21
|
end
|
21
22
|
|
23
|
+
self.consumer_token = 'gOhjax6s0L3mLeaTtBWPw'
|
24
|
+
self.consumer_secret = 'yz4gpAVXJHKxvsGK85tEyzQJ7o2FEy27H1KEWL75jfA'
|
25
|
+
|
22
26
|
def valid?
|
23
27
|
valid = true
|
28
|
+
valid &= complain("Attribute 'consumer_token' must be specified", self) unless arg(:consumer_token)
|
29
|
+
valid &= complain("Attribute 'consumer_secret' must be specified", self) unless arg(:consumer_secret)
|
30
|
+
valid &= complain("Attribute 'access_token' must be specified", self) unless arg(:access_token)
|
31
|
+
valid &= complain("Attribute 'access_secret' must be specified", self) unless arg(:access_secret)
|
32
|
+
valid
|
24
33
|
end
|
25
34
|
|
35
|
+
attr_accessor :consumer_token, :consumer_secret,
|
36
|
+
:access_token, :access_secret
|
37
|
+
|
26
38
|
def notify(message, time, priority, category, host)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
39
|
+
oauth = ::Twitter::OAuth.new(arg(:consumer_token), arg(:consumer_secret))
|
40
|
+
oauth.authorize_from_access(arg(:access_token), arg(:access_secret))
|
41
|
+
|
42
|
+
::Twitter::Base.new(oauth).update(message)
|
43
|
+
|
44
|
+
self.info = "sent twitter update"
|
45
|
+
rescue => e
|
46
|
+
applog(nil, :info, "failed to send twitter update: #{e.message}")
|
47
|
+
applog(nil, :debug, e.backtrace.join("\n"))
|
35
48
|
end
|
36
49
|
end
|
37
50
|
end
|
data/lib/god/contacts/webhook.rb
CHANGED
@@ -1,44 +1,70 @@
|
|
1
|
-
#
|
1
|
+
# Send a notice to a webhook.
|
2
2
|
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# c.hook_url = 'http://hook/url'
|
6
|
-
# end
|
3
|
+
# url - The String webhook URL.
|
4
|
+
# format - The Symbol format [ :form | :json ] (default: :form).
|
7
5
|
|
8
6
|
require 'net/http'
|
9
7
|
require 'uri'
|
10
8
|
|
9
|
+
CONTACT_DEPS[:webhook] = ['json']
|
10
|
+
CONTACT_DEPS[:webhook].each do |d|
|
11
|
+
require d
|
12
|
+
end
|
13
|
+
|
11
14
|
module God
|
12
15
|
module Contacts
|
13
16
|
|
14
17
|
class Webhook < Contact
|
18
|
+
class << self
|
19
|
+
attr_accessor :url, :format
|
20
|
+
end
|
15
21
|
|
16
|
-
|
22
|
+
self.format = :form
|
17
23
|
|
18
24
|
def valid?
|
19
25
|
valid = true
|
26
|
+
valid &= complain("Attribute 'url' must be specified", self) unless arg(:url)
|
27
|
+
valid &= complain("Attribute 'format' must be one of [ :form | :json ]", self) unless [:form, :json].include?(arg(:format))
|
28
|
+
valid
|
20
29
|
end
|
21
30
|
|
31
|
+
attr_accessor :url, :format
|
32
|
+
|
22
33
|
def notify(message, time, priority, category, host)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
data = {
|
35
|
+
:message => message,
|
36
|
+
:time => time,
|
37
|
+
:priority => priority,
|
38
|
+
:category => category,
|
39
|
+
:host => host
|
40
|
+
}
|
41
|
+
|
42
|
+
uri = URI.parse(arg(:url))
|
43
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
44
|
+
|
45
|
+
req = nil
|
46
|
+
res = nil
|
47
|
+
|
48
|
+
case arg(:format)
|
49
|
+
when :form
|
50
|
+
req = Net::HTTP::Post.new(uri.path)
|
51
|
+
req.set_form_data(data)
|
52
|
+
when :json
|
53
|
+
req = Net::HTTP::Post.new(uri.path)
|
54
|
+
req.body = data.to_json
|
55
|
+
end
|
56
|
+
|
57
|
+
res = http.request(req)
|
58
|
+
|
59
|
+
case res
|
60
|
+
when Net::HTTPSuccess
|
61
|
+
self.info = "sent webhook to #{arg(:url)}"
|
62
|
+
else
|
63
|
+
self.info = "failed to send webhook to #{arg(:url)}: #{res.error!}"
|
41
64
|
end
|
65
|
+
rescue Object => e
|
66
|
+
applog(nil, :info, "failed to send email to #{arg(:url)}: #{e.message}")
|
67
|
+
applog(nil, :debug, e.backtrace.join("\n"))
|
42
68
|
end
|
43
69
|
|
44
70
|
end
|