resurrected_god 0.14.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.
- checksums.yaml +7 -0
- data/Announce.txt +135 -0
- data/Gemfile +5 -0
- data/LICENSE +22 -0
- data/README.md +33 -0
- data/Rakefile +129 -0
- data/bin/god +134 -0
- data/doc/god.asciidoc +1592 -0
- data/doc/intro.asciidoc +20 -0
- data/ext/god/.gitignore +5 -0
- data/ext/god/extconf.rb +56 -0
- data/ext/god/kqueue_handler.c +133 -0
- data/ext/god/netlink_handler.c +182 -0
- data/lib/god/behavior.rb +52 -0
- data/lib/god/behaviors/clean_pid_file.rb +21 -0
- data/lib/god/behaviors/clean_unix_socket.rb +21 -0
- data/lib/god/behaviors/notify_when_flapping.rb +51 -0
- data/lib/god/cli/command.rb +268 -0
- data/lib/god/cli/run.rb +170 -0
- data/lib/god/cli/version.rb +23 -0
- data/lib/god/compat19.rb +33 -0
- data/lib/god/condition.rb +96 -0
- data/lib/god/conditions/always.rb +36 -0
- data/lib/god/conditions/complex.rb +86 -0
- data/lib/god/conditions/cpu_usage.rb +80 -0
- data/lib/god/conditions/degrading_lambda.rb +52 -0
- data/lib/god/conditions/disk_usage.rb +32 -0
- data/lib/god/conditions/file_mtime.rb +28 -0
- data/lib/god/conditions/file_touched.rb +44 -0
- data/lib/god/conditions/flapping.rb +128 -0
- data/lib/god/conditions/http_response_code.rb +184 -0
- data/lib/god/conditions/lambda.rb +25 -0
- data/lib/god/conditions/memory_usage.rb +82 -0
- data/lib/god/conditions/process_exits.rb +66 -0
- data/lib/god/conditions/process_running.rb +63 -0
- data/lib/god/conditions/socket_responding.rb +142 -0
- data/lib/god/conditions/tries.rb +44 -0
- data/lib/god/configurable.rb +57 -0
- data/lib/god/contact.rb +114 -0
- data/lib/god/contacts/airbrake.rb +44 -0
- data/lib/god/contacts/campfire.rb +121 -0
- data/lib/god/contacts/email.rb +130 -0
- data/lib/god/contacts/hipchat.rb +117 -0
- data/lib/god/contacts/jabber.rb +75 -0
- data/lib/god/contacts/prowl.rb +57 -0
- data/lib/god/contacts/scout.rb +55 -0
- data/lib/god/contacts/sensu.rb +59 -0
- data/lib/god/contacts/slack.rb +98 -0
- data/lib/god/contacts/statsd.rb +46 -0
- data/lib/god/contacts/twitter.rb +51 -0
- data/lib/god/contacts/webhook.rb +74 -0
- data/lib/god/driver.rb +238 -0
- data/lib/god/errors.rb +24 -0
- data/lib/god/event_handler.rb +112 -0
- data/lib/god/event_handlers/dummy_handler.rb +13 -0
- data/lib/god/event_handlers/kqueue_handler.rb +17 -0
- data/lib/god/event_handlers/netlink_handler.rb +13 -0
- data/lib/god/logger.rb +109 -0
- data/lib/god/metric.rb +87 -0
- data/lib/god/process.rb +381 -0
- data/lib/god/registry.rb +32 -0
- data/lib/god/simple_logger.rb +59 -0
- data/lib/god/socket.rb +113 -0
- data/lib/god/sugar.rb +62 -0
- data/lib/god/sys_logger.rb +45 -0
- data/lib/god/system/portable_poller.rb +42 -0
- data/lib/god/system/process.rb +50 -0
- data/lib/god/system/slash_proc_poller.rb +92 -0
- data/lib/god/task.rb +552 -0
- data/lib/god/timeline.rb +25 -0
- data/lib/god/trigger.rb +43 -0
- data/lib/god/version.rb +4 -0
- data/lib/god/watch.rb +340 -0
- data/lib/god.rb +777 -0
- data/test/configs/child_events/child_events.god +44 -0
- data/test/configs/child_events/simple_server.rb +3 -0
- data/test/configs/child_polls/child_polls.god +37 -0
- data/test/configs/child_polls/simple_server.rb +12 -0
- data/test/configs/complex/complex.god +59 -0
- data/test/configs/complex/simple_server.rb +3 -0
- data/test/configs/contact/contact.god +118 -0
- data/test/configs/contact/simple_server.rb +3 -0
- data/test/configs/daemon_events/daemon_events.god +37 -0
- data/test/configs/daemon_events/simple_server.rb +8 -0
- data/test/configs/daemon_events/simple_server_stop.rb +11 -0
- data/test/configs/daemon_polls/daemon_polls.god +17 -0
- data/test/configs/daemon_polls/simple_server.rb +6 -0
- data/test/configs/degrading_lambda/degrading_lambda.god +31 -0
- data/test/configs/degrading_lambda/tcp_server.rb +15 -0
- data/test/configs/keepalive/keepalive.god +9 -0
- data/test/configs/keepalive/keepalive.rb +12 -0
- data/test/configs/lifecycle/lifecycle.god +25 -0
- data/test/configs/matias/matias.god +50 -0
- data/test/configs/real.rb +59 -0
- data/test/configs/running_load/running_load.god +16 -0
- data/test/configs/stop_options/simple_server.rb +12 -0
- data/test/configs/stop_options/stop_options.god +39 -0
- data/test/configs/stress/simple_server.rb +3 -0
- data/test/configs/stress/stress.god +15 -0
- data/test/configs/task/logs/.placeholder +0 -0
- data/test/configs/task/task.god +26 -0
- data/test/configs/test.rb +61 -0
- data/test/configs/usr1_trapper.rb +10 -0
- data/test/helper.rb +172 -0
- data/test/suite.rb +6 -0
- data/test/test_airbrake.rb +14 -0
- data/test/test_behavior.rb +18 -0
- data/test/test_campfire.rb +22 -0
- data/test/test_condition.rb +52 -0
- data/test/test_conditions_disk_usage.rb +50 -0
- data/test/test_conditions_http_response_code.rb +109 -0
- data/test/test_conditions_process_running.rb +40 -0
- data/test/test_conditions_socket_responding.rb +176 -0
- data/test/test_conditions_tries.rb +67 -0
- data/test/test_contact.rb +109 -0
- data/test/test_driver.rb +26 -0
- data/test/test_email.rb +34 -0
- data/test/test_event_handler.rb +82 -0
- data/test/test_god.rb +710 -0
- data/test/test_god_system.rb +201 -0
- data/test/test_handlers_kqueue_handler.rb +16 -0
- data/test/test_hipchat.rb +23 -0
- data/test/test_jabber.rb +29 -0
- data/test/test_logger.rb +55 -0
- data/test/test_metric.rb +74 -0
- data/test/test_process.rb +263 -0
- data/test/test_prowl.rb +15 -0
- data/test/test_registry.rb +15 -0
- data/test/test_sensu.rb +11 -0
- data/test/test_slack.rb +57 -0
- data/test/test_socket.rb +34 -0
- data/test/test_statsd.rb +22 -0
- data/test/test_sugar.rb +42 -0
- data/test/test_system_portable_poller.rb +17 -0
- data/test/test_system_process.rb +30 -0
- data/test/test_task.rb +246 -0
- data/test/test_timeline.rb +37 -0
- data/test/test_trigger.rb +63 -0
- data/test/test_watch.rb +286 -0
- data/test/test_webhook.rb +22 -0
- metadata +476 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Send a notice to an email address.
|
|
2
|
+
#
|
|
3
|
+
# to_email - The String email address to which the email will be sent.
|
|
4
|
+
# to_name - The String name corresponding to the recipient.
|
|
5
|
+
# from_email - The String email address from which the email will be sent.
|
|
6
|
+
# from_name - The String name corresponding to the sender.
|
|
7
|
+
# delivery_method - The Symbol delivery method. [ :smtp | :sendmail ]
|
|
8
|
+
# (default: :smtp).
|
|
9
|
+
#
|
|
10
|
+
# === SMTP Options (when delivery_method = :smtp) ===
|
|
11
|
+
# server_host - The String hostname of the SMTP server (default: localhost).
|
|
12
|
+
# server_port - The Integer port of the SMTP server (default: 25).
|
|
13
|
+
# server_auth - The Symbol authentication method. Possible values:
|
|
14
|
+
# [ nil | :plain | :login | :cram_md5 ]
|
|
15
|
+
# The default is nil, which means no authentication. To
|
|
16
|
+
# enable authentication, pass the appropriate symbol and
|
|
17
|
+
# then pass the appropriate SMTP Auth Options (below).
|
|
18
|
+
#
|
|
19
|
+
# === SMTP Auth Options (when server_auth != nil) ===
|
|
20
|
+
# server_domain - The String domain.
|
|
21
|
+
# server_user - The String username.
|
|
22
|
+
# server_password - The String password.
|
|
23
|
+
#
|
|
24
|
+
# === Sendmail Options (when delivery_method = :sendmail) ===
|
|
25
|
+
# sendmail_path - The String path to the sendmail executable
|
|
26
|
+
# (default: "/usr/sbin/sendmail").
|
|
27
|
+
# sendmail_args - The String args to send to sendmail (default "-i -t").
|
|
28
|
+
|
|
29
|
+
require 'time'
|
|
30
|
+
require 'net/smtp'
|
|
31
|
+
|
|
32
|
+
module God
|
|
33
|
+
module Contacts
|
|
34
|
+
|
|
35
|
+
class Email < Contact
|
|
36
|
+
class << self
|
|
37
|
+
attr_accessor :to_email, :to_name, :from_email, :from_name,
|
|
38
|
+
:delivery_method, :server_host, :server_port,
|
|
39
|
+
:server_auth, :server_domain, :server_user,
|
|
40
|
+
:server_password, :sendmail_path, :sendmail_args
|
|
41
|
+
attr_accessor :format
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
self.from_email = 'god@example.com'
|
|
45
|
+
self.from_name = 'God Process Monitoring'
|
|
46
|
+
self.delivery_method = :smtp
|
|
47
|
+
self.server_auth = nil
|
|
48
|
+
self.server_host = 'localhost'
|
|
49
|
+
self.server_port = 25
|
|
50
|
+
self.sendmail_path = '/usr/sbin/sendmail'
|
|
51
|
+
self.sendmail_args = '-i -t'
|
|
52
|
+
|
|
53
|
+
self.format = lambda do |name, from_email, from_name, to_email, to_name, message, time, priority, category, host|
|
|
54
|
+
<<-EOF
|
|
55
|
+
From: #{from_name} <#{from_email}>
|
|
56
|
+
To: #{to_name || name} <#{to_email}>
|
|
57
|
+
Subject: [god] #{message}
|
|
58
|
+
Date: #{time.httpdate}
|
|
59
|
+
Message-Id: <#{rand(1000000000).to_s(36)}.#{$$}.#{from_email}>
|
|
60
|
+
|
|
61
|
+
Message: #{message}
|
|
62
|
+
Host: #{host}
|
|
63
|
+
Priority: #{priority}
|
|
64
|
+
Category: #{category}
|
|
65
|
+
EOF
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
attr_accessor :to_email, :to_name, :from_email, :from_name,
|
|
69
|
+
:delivery_method, :server_host, :server_port,
|
|
70
|
+
:server_auth, :server_domain, :server_user,
|
|
71
|
+
:server_password, :sendmail_path, :sendmail_args
|
|
72
|
+
|
|
73
|
+
def valid?
|
|
74
|
+
valid = true
|
|
75
|
+
valid &= complain("Attribute 'to_email' must be specified", self) unless arg(:to_email)
|
|
76
|
+
valid &= complain("Attribute 'delivery_method' must be one of [ :smtp | :sendmail ]", self) unless [:smtp, :sendmail].include?(arg(:delivery_method))
|
|
77
|
+
if arg(:delivery_method) == :smtp
|
|
78
|
+
valid &= complain("Attribute 'server_host' must be specified", self) unless arg(:server_host)
|
|
79
|
+
valid &= complain("Attribute 'server_port' must be specified", self) unless arg(:server_port)
|
|
80
|
+
if arg(:server_auth)
|
|
81
|
+
valid &= complain("Attribute 'server_domain' must be specified", self) unless arg(:server_domain)
|
|
82
|
+
valid &= complain("Attribute 'server_user' must be specified", self) unless arg(:server_user)
|
|
83
|
+
valid &= complain("Attribute 'server_password' must be specified", self) unless arg(:server_password)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
valid
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def notify(message, time, priority, category, host)
|
|
90
|
+
body = Email.format.call(self.name, arg(:from_email), arg(:from_name),
|
|
91
|
+
arg(:to_email), arg(:to_name), message, time,
|
|
92
|
+
priority, category, host)
|
|
93
|
+
|
|
94
|
+
case arg(:delivery_method)
|
|
95
|
+
when :smtp
|
|
96
|
+
notify_smtp(body)
|
|
97
|
+
when :sendmail
|
|
98
|
+
notify_sendmail(body)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
self.info = "sent email to #{arg(:to_email)} via #{arg(:delivery_method).to_s}"
|
|
102
|
+
rescue Object => e
|
|
103
|
+
applog(nil, :info, "failed to send email to #{arg(:to_email)} via #{arg(:delivery_method).to_s}: #{e.message}")
|
|
104
|
+
applog(nil, :debug, e.backtrace.join("\n"))
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def notify_smtp(mail)
|
|
108
|
+
args = [arg(:server_host), arg(:server_port)]
|
|
109
|
+
if arg(:server_auth)
|
|
110
|
+
args << arg(:server_domain)
|
|
111
|
+
args << arg(:server_user)
|
|
112
|
+
args << arg(:server_password)
|
|
113
|
+
args << arg(:server_auth)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
Net::SMTP.start(*args) do |smtp|
|
|
117
|
+
smtp.send_message(mail, arg(:from_email), arg(:to_email))
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def notify_sendmail(mail)
|
|
122
|
+
IO.popen("#{arg(:sendmail_path)} #{arg(:sendmail_args)}","w+") do |sm|
|
|
123
|
+
sm.print(mail.gsub(/\r/, ''))
|
|
124
|
+
sm.flush
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Send a notice to a Hipchat room (http://hipchat.com).
|
|
2
|
+
#
|
|
3
|
+
# token - The String token used for authentication.
|
|
4
|
+
# room - The String room name to which the message should be sent.
|
|
5
|
+
# ssl - A Boolean determining whether or not to use SSL
|
|
6
|
+
# (default: false).
|
|
7
|
+
# from - The String representing who the message should be sent as.
|
|
8
|
+
|
|
9
|
+
require 'net/http'
|
|
10
|
+
require 'net/https'
|
|
11
|
+
|
|
12
|
+
CONTACT_DEPS[:hipchat] = ['json']
|
|
13
|
+
CONTACT_DEPS[:hipchat].each do |d|
|
|
14
|
+
require d
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module Marshmallow
|
|
18
|
+
class Connection
|
|
19
|
+
def initialize(options)
|
|
20
|
+
raise "Required option :token not set." unless options[:token]
|
|
21
|
+
@options = options
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def base_url
|
|
25
|
+
scheme = @options[:ssl] ? 'https' : 'http'
|
|
26
|
+
"#{scheme}://api.hipchat.com/v1/rooms"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def find_room_id_by_name(room_name)
|
|
30
|
+
url = URI.parse("#{base_url}/list?format=json&auth_token=#{@options[:token]}")
|
|
31
|
+
http = Net::HTTP.new(url.host, url.port)
|
|
32
|
+
http.use_ssl = true if @options[:ssl]
|
|
33
|
+
|
|
34
|
+
req = Net::HTTP::Get.new(url.request_uri)
|
|
35
|
+
req.set_content_type('application/json')
|
|
36
|
+
|
|
37
|
+
res = http.request(req)
|
|
38
|
+
case res
|
|
39
|
+
when Net::HTTPSuccess
|
|
40
|
+
rooms = JSON.parse(res.body)
|
|
41
|
+
room = rooms['rooms'].select { |x| x['name'] == room_name }
|
|
42
|
+
rooms.empty? ? nil : room.first['room_id'].to_i
|
|
43
|
+
else
|
|
44
|
+
raise res.error!
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def speak(room, message)
|
|
49
|
+
room_id = find_room_id_by_name(room)
|
|
50
|
+
puts "in spark: room_id = #{room_id}"
|
|
51
|
+
raise "No such room: #{room}." unless room_id
|
|
52
|
+
|
|
53
|
+
escaped_message = URI.escape(message)
|
|
54
|
+
|
|
55
|
+
url = URI.parse("#{base_url}/message?message_format=text&format=json&auth_token=#{@options[:token]}&from=#{@options[:from]}&room_id=#{room}&message=#{escaped_message}")
|
|
56
|
+
|
|
57
|
+
http = Net::HTTP.new(url.host, url.port)
|
|
58
|
+
http.use_ssl = true if @options[:ssl]
|
|
59
|
+
|
|
60
|
+
req = Net::HTTP::Post.new(url.request_uri)
|
|
61
|
+
req.set_content_type('application/json')
|
|
62
|
+
res = http.request(req)
|
|
63
|
+
case res
|
|
64
|
+
when Net::HTTPSuccess
|
|
65
|
+
true
|
|
66
|
+
else
|
|
67
|
+
raise res.error!
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
module God
|
|
74
|
+
module Contacts
|
|
75
|
+
|
|
76
|
+
class Hipchat < Contact
|
|
77
|
+
class << self
|
|
78
|
+
attr_accessor :token, :room, :ssl, :from
|
|
79
|
+
attr_accessor :format
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
self.ssl = false
|
|
83
|
+
|
|
84
|
+
self.format = lambda do |message, time, priority, category, host|
|
|
85
|
+
"[#{time.strftime('%H:%M:%S')}] #{host} - #{message}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
attr_accessor :token, :room, :ssl, :from
|
|
89
|
+
|
|
90
|
+
def valid?
|
|
91
|
+
valid = true
|
|
92
|
+
valid &= complain("Attribute 'token' must be specified", self) unless arg(:token)
|
|
93
|
+
valid &= complain("Attribute 'room' must be specified", self) unless arg(:room)
|
|
94
|
+
valid &= complain("Attribute 'from' must be specified", self) unless arg(:from)
|
|
95
|
+
valid
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def notify(message, time, priority, category, host)
|
|
99
|
+
body = Hipchat.format.call(message, time, priority, category, host)
|
|
100
|
+
|
|
101
|
+
conn = Marshmallow::Connection.new(
|
|
102
|
+
:token => arg(:token),
|
|
103
|
+
:ssl => arg(:ssl),
|
|
104
|
+
:from => arg(:from)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
conn.speak(arg(:room), body)
|
|
108
|
+
|
|
109
|
+
self.info = "notified hipchat: #{arg(:room)}"
|
|
110
|
+
rescue Object => e
|
|
111
|
+
applog(nil, :info, "failed to notify hipchat: #{e.message}")
|
|
112
|
+
applog(nil, :debug, e.backtrace.join("\n"))
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Send a notice to a Jabber address.
|
|
2
|
+
#
|
|
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").
|
|
9
|
+
|
|
10
|
+
CONTACT_DEPS[:jabber] = ['xmpp4r']
|
|
11
|
+
CONTACT_DEPS[:jabber].each do |d|
|
|
12
|
+
require d
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module God
|
|
16
|
+
module Contacts
|
|
17
|
+
|
|
18
|
+
class Jabber < Contact
|
|
19
|
+
class << self
|
|
20
|
+
attr_accessor :host, :port, :from_jid, :password, :to_jid, :subject
|
|
21
|
+
attr_accessor :format
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
self.port = 5222
|
|
25
|
+
self.subject = 'God Notification'
|
|
26
|
+
|
|
27
|
+
self.format = lambda do |message, time, priority, category, host|
|
|
28
|
+
text = "Message: #{message}\n"
|
|
29
|
+
text += "Host: #{host}\n" if host
|
|
30
|
+
text += "Priority: #{priority}\n" if priority
|
|
31
|
+
text += "Category: #{category}\n" if category
|
|
32
|
+
text
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
attr_accessor :host, :port, :from_jid, :password, :to_jid, :subject
|
|
36
|
+
|
|
37
|
+
def valid?
|
|
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
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def notify(message, time, priority, category, host)
|
|
48
|
+
body = Jabber.format.call(message, time, priority, category, host)
|
|
49
|
+
|
|
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))
|
|
54
|
+
|
|
55
|
+
jabber_id = ::Jabber::JID.new("#{arg(:from_jid)}/God")
|
|
56
|
+
|
|
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"))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Send a notice to Prowl (http://prowl.weks.net/).
|
|
2
|
+
#
|
|
3
|
+
# apikey - The String API key.
|
|
4
|
+
|
|
5
|
+
CONTACT_DEPS[:prowl] = ['prowly']
|
|
6
|
+
CONTACT_DEPS[:prowl].each do |d|
|
|
7
|
+
require d
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module God
|
|
11
|
+
module Contacts
|
|
12
|
+
class Prowl < Contact
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
attr_accessor :apikey
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def valid?
|
|
19
|
+
valid = true
|
|
20
|
+
valid &= complain("Attribute 'apikey' must be specified", self) if self.apikey.nil?
|
|
21
|
+
valid
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
attr_accessor :apikey
|
|
25
|
+
|
|
26
|
+
def notify(message, time, priority, category, host)
|
|
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
|
|
34
|
+
|
|
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}"
|
|
39
|
+
end
|
|
40
|
+
rescue Object => e
|
|
41
|
+
applog(nil, :info, "failed to send prowl notification to #{self.name}: #{e.message}")
|
|
42
|
+
applog(nil, :debug, e.backtrace.join("\n"))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def map_priority(priority)
|
|
46
|
+
case priority
|
|
47
|
+
when 1 then Prowly::Notification::Priority::EMERGENCY
|
|
48
|
+
when 2 then Prowly::Notification::Priority::HIGH
|
|
49
|
+
when 3 then Prowly::Notification::Priority::NORMAL
|
|
50
|
+
when 4 then Prowly::Notification::Priority::MODERATE
|
|
51
|
+
when 5 then Prowly::Notification::Priority::VERY_LOW
|
|
52
|
+
else Prowly::Notification::Priority::NORMAL
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Send a notice to Scout (http://scoutapp.com/).
|
|
2
|
+
#
|
|
3
|
+
# client_key - The String client key.
|
|
4
|
+
# plugin_id - The String plugin id.
|
|
5
|
+
|
|
6
|
+
require 'net/http'
|
|
7
|
+
require 'uri'
|
|
8
|
+
|
|
9
|
+
module God
|
|
10
|
+
module Contacts
|
|
11
|
+
|
|
12
|
+
class Scout < Contact
|
|
13
|
+
class << self
|
|
14
|
+
attr_accessor :client_key, :plugin_id
|
|
15
|
+
attr_accessor :format
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
self.format = lambda do |message, priority, category, host|
|
|
19
|
+
text = "Message: #{message}\n"
|
|
20
|
+
text += "Host: #{host}\n" if host
|
|
21
|
+
text += "Priority: #{priority}\n" if priority
|
|
22
|
+
text += "Category: #{category}\n" if category
|
|
23
|
+
return text
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_accessor :client_key, :plugin_id
|
|
27
|
+
|
|
28
|
+
def valid?
|
|
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
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def notify(message, time, priority, category, host)
|
|
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
|
+
}
|
|
43
|
+
|
|
44
|
+
uri = URI.parse('http://scoutapp.com/alerts/create')
|
|
45
|
+
Net::HTTP.post_form(uri, data)
|
|
46
|
+
|
|
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"))
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
2
|
+
# Send a notice to a SENSU client socket, port 3030 on 'localhost' only.
|
|
3
|
+
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
4
|
+
# [mandatory]
|
|
5
|
+
# check_name - a unique check name
|
|
6
|
+
#
|
|
7
|
+
# [optional]
|
|
8
|
+
# status_code - status codes used are 0 for OK, 1 for WARNING, 2 for CRITICAL, and 3 or greater to indicate UNKNOWN or CUSTOM.
|
|
9
|
+
# handler - default handler
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
CONTACT_DEPS[:sensu] = ['json']
|
|
13
|
+
CONTACT_DEPS[:sensu].each do |d|
|
|
14
|
+
require d
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module God
|
|
18
|
+
module Contacts
|
|
19
|
+
|
|
20
|
+
class Sensu < Contact
|
|
21
|
+
class << self
|
|
22
|
+
attr_accessor :check_name, :status_code, :handler, :host, :port
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
self.status_code = 2
|
|
26
|
+
self.handler = 'default'
|
|
27
|
+
self.host = 'localhost'
|
|
28
|
+
self.port = 3030
|
|
29
|
+
|
|
30
|
+
def valid?
|
|
31
|
+
valid = true
|
|
32
|
+
valid &= complain("Attribute 'check_name' must be specified", self) unless arg(:check_name)
|
|
33
|
+
valid
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
attr_accessor :check_name, :status_code, :handler, :host, :port
|
|
37
|
+
|
|
38
|
+
def sensu_client_socket(msg)
|
|
39
|
+
u = UDPSocket.new
|
|
40
|
+
u.send(msg + "\n", 0, arg(:host).nil? ? self.host : arg(:host), arg(:port).nil? ? self.port : arg(:port))
|
|
41
|
+
u.close
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def notify(message, time, priority, category, host)
|
|
45
|
+
data = {
|
|
46
|
+
:category => category,
|
|
47
|
+
:message => message,
|
|
48
|
+
:priority => priority,
|
|
49
|
+
:host => host,
|
|
50
|
+
:time => time,
|
|
51
|
+
}
|
|
52
|
+
parcel = { 'name' => arg(:check_name), 'status' => arg(:status_code).nil? ? self.status_code : arg(:status_code), 'output' => data.to_json, 'handler' => arg(:handler).empty? ? self.handler : arg(:handler), 'executed' => Time.now.to_i }
|
|
53
|
+
sensu_client_socket parcel.to_json
|
|
54
|
+
self.info = "notified sensu: #{arg(:check_name)}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Send a message to a Slack channel
|
|
2
|
+
#
|
|
3
|
+
# account - The name of your Slack account (visible in URL, e.g. foo.slack.com)
|
|
4
|
+
# token - The token of the webhook created in Slack
|
|
5
|
+
# channel - The name of the channel to send the message to, prefixed with #
|
|
6
|
+
# notify_channel - Whether to send an "@channel" in the message, to alert everyone in the channel
|
|
7
|
+
# format - An optional format string to change how the alert is displayed
|
|
8
|
+
|
|
9
|
+
require 'net/http'
|
|
10
|
+
require 'uri'
|
|
11
|
+
|
|
12
|
+
CONTACT_DEPS[:slack] = ['json']
|
|
13
|
+
CONTACT_DEPS[:slack].each do |d|
|
|
14
|
+
require d
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module God
|
|
18
|
+
module Contacts
|
|
19
|
+
|
|
20
|
+
class Slack < Contact
|
|
21
|
+
class << self
|
|
22
|
+
attr_accessor :url, :channel, :notify_channel, :format, :username, :emoji
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
self.channel = "#general"
|
|
26
|
+
self.notify_channel = false
|
|
27
|
+
self.format = "%{priority} alert on %{host}: %{message} (%{category}, %{time})"
|
|
28
|
+
|
|
29
|
+
def valid?
|
|
30
|
+
valid = true
|
|
31
|
+
valid &= complain("Attribute 'url' must be specified", self) unless arg(:url)
|
|
32
|
+
valid
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
attr_accessor :url, :channel, :notify_channel, :format, :username, :emoji
|
|
36
|
+
|
|
37
|
+
def text(data)
|
|
38
|
+
text = ""
|
|
39
|
+
text << "<!channel> " if arg(:notify_channel)
|
|
40
|
+
|
|
41
|
+
if RUBY_VERSION =~ /^1\.8/
|
|
42
|
+
text << arg(:format).gsub(/%\{(\w+)\}/) do |match|
|
|
43
|
+
data[$1.to_sym]
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
text << arg(:format) % data
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
text
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def notify(message, time, priority, category, host)
|
|
53
|
+
text = text({
|
|
54
|
+
:message => message,
|
|
55
|
+
:time => time,
|
|
56
|
+
:priority => priority,
|
|
57
|
+
:category => category,
|
|
58
|
+
:host => host
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
request(text)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def api_url
|
|
65
|
+
URI.parse arg(:url)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def request(text)
|
|
69
|
+
http = Net::HTTP.new(api_url.host, api_url.port)
|
|
70
|
+
http.use_ssl = true
|
|
71
|
+
|
|
72
|
+
req = Net::HTTP::Post.new(api_url.request_uri)
|
|
73
|
+
req.body = {
|
|
74
|
+
:link_names => 1,
|
|
75
|
+
:text => text,
|
|
76
|
+
:channel => arg(:channel)
|
|
77
|
+
}.tap { |payload|
|
|
78
|
+
payload[:username] = arg(:username) if arg(:username)
|
|
79
|
+
payload[:icon_emoji] = arg(:emoji) if arg(:emoji)
|
|
80
|
+
}.to_json
|
|
81
|
+
|
|
82
|
+
res = http.request(req)
|
|
83
|
+
|
|
84
|
+
case res
|
|
85
|
+
when Net::HTTPSuccess
|
|
86
|
+
self.info = "successfully notified slack on channel #{arg(:channel)}"
|
|
87
|
+
else
|
|
88
|
+
self.info = "failed to send webhook to #{arg(:url)}: #{res.error!}"
|
|
89
|
+
end
|
|
90
|
+
rescue Object => e
|
|
91
|
+
applog(nil, :info, "failed to send webhook to #{arg(:url)}: #{e.message}")
|
|
92
|
+
applog(nil, :debug, e.backtrace.join("\n"))
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Send a notice to statsd
|
|
2
|
+
#
|
|
3
|
+
# host - statsd host
|
|
4
|
+
# port - statsd port (optional)
|
|
5
|
+
|
|
6
|
+
require 'statsd-ruby'
|
|
7
|
+
|
|
8
|
+
module God
|
|
9
|
+
module Contacts
|
|
10
|
+
|
|
11
|
+
class Statsd < Contact
|
|
12
|
+
class << self
|
|
13
|
+
attr_accessor :host, :port
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_accessor :host, :port
|
|
17
|
+
|
|
18
|
+
def valid?
|
|
19
|
+
valid = true
|
|
20
|
+
valid &= complain("Attribute 'statsd_host' must be specified", self) unless arg(:host)
|
|
21
|
+
valid
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def notify(message, time, priority, category, hostname)
|
|
25
|
+
statsd = ::Statsd.new host, (port ? port.to_i : 8125) # 8125 is the default statsd port
|
|
26
|
+
|
|
27
|
+
hostname.gsub! /\./, '_'
|
|
28
|
+
app = message.gsub /([^\s]*).*/, '\1'
|
|
29
|
+
|
|
30
|
+
[
|
|
31
|
+
'cpu out of bounds',
|
|
32
|
+
'memory out of bounds',
|
|
33
|
+
'process is flapping'
|
|
34
|
+
].each do |event_type|
|
|
35
|
+
statsd.increment "god.#{event_type.gsub(/\s/, '_')}.#{hostname}.#{app}" if message.include? event_type
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
self.info = 'sent statsd alert'
|
|
39
|
+
rescue => e
|
|
40
|
+
applog(nil, :info, "failed to send statsd alert: #{e.message}")
|
|
41
|
+
applog(nil, :debug, e.backtrace.join("\n"))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Send a notice to a Twitter account (http://twitter.com/).
|
|
2
|
+
#
|
|
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.
|
|
9
|
+
|
|
10
|
+
CONTACT_DEPS[:twitter] = ['twitter']
|
|
11
|
+
CONTACT_DEPS[:twitter].each do |d|
|
|
12
|
+
require d
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module God
|
|
16
|
+
module Contacts
|
|
17
|
+
class Twitter < Contact
|
|
18
|
+
class << self
|
|
19
|
+
attr_accessor :consumer_token, :consumer_secret,
|
|
20
|
+
:access_token, :access_secret
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
self.consumer_token = 'gOhjax6s0L3mLeaTtBWPw'
|
|
24
|
+
self.consumer_secret = 'yz4gpAVXJHKxvsGK85tEyzQJ7o2FEy27H1KEWL75jfA'
|
|
25
|
+
|
|
26
|
+
def valid?
|
|
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
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
attr_accessor :consumer_token, :consumer_secret,
|
|
36
|
+
:access_token, :access_secret
|
|
37
|
+
|
|
38
|
+
def notify(message, time, priority, category, host)
|
|
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"))
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|