god 0.10.1 → 0.11.0
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.
- 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
|