mcproc 2016.2.20
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 +9 -0
- data/History.txt +469 -0
- data/LICENSE +22 -0
- data/README.md +37 -0
- data/Rakefile +185 -0
- data/TODO.md +37 -0
- data/bin/mcproc +134 -0
- data/doc/intro.asciidoc +20 -0
- data/doc/mcproc.asciidoc +1592 -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.rb +780 -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/watch.rb +340 -0
- data/mcproc.gemspec +192 -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 +475 -0
@@ -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
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Send a notice to a webhook.
|
2
|
+
#
|
3
|
+
# url - The String webhook URL.
|
4
|
+
# format - The Symbol format [ :form | :json ] (default: :form).
|
5
|
+
|
6
|
+
require 'net/http'
|
7
|
+
require 'uri'
|
8
|
+
|
9
|
+
CONTACT_DEPS[:webhook] = ['json']
|
10
|
+
CONTACT_DEPS[:webhook].each do |d|
|
11
|
+
require d
|
12
|
+
end
|
13
|
+
|
14
|
+
module God
|
15
|
+
module Contacts
|
16
|
+
|
17
|
+
class Webhook < Contact
|
18
|
+
class << self
|
19
|
+
attr_accessor :url, :format
|
20
|
+
end
|
21
|
+
|
22
|
+
self.format = :form
|
23
|
+
|
24
|
+
def valid?
|
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
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_accessor :url, :format
|
32
|
+
|
33
|
+
def notify(message, time, priority, category, host)
|
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
|
+
http.use_ssl = true if uri.scheme == "https"
|
45
|
+
|
46
|
+
req = nil
|
47
|
+
res = nil
|
48
|
+
|
49
|
+
case arg(:format)
|
50
|
+
when :form
|
51
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
52
|
+
req.set_form_data(data)
|
53
|
+
when :json
|
54
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
55
|
+
req.body = data.to_json
|
56
|
+
end
|
57
|
+
|
58
|
+
res = http.request(req)
|
59
|
+
|
60
|
+
case res
|
61
|
+
when Net::HTTPSuccess
|
62
|
+
self.info = "sent webhook to #{arg(:url)}"
|
63
|
+
else
|
64
|
+
self.info = "failed to send webhook to #{arg(:url)}: #{res.error!}"
|
65
|
+
end
|
66
|
+
rescue Object => e
|
67
|
+
applog(nil, :info, "failed to send webhook to #{arg(:url)}: #{e.message}")
|
68
|
+
applog(nil, :debug, e.backtrace.join("\n"))
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|