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.
Files changed (141) hide show
  1. checksums.yaml +7 -0
  2. data/Announce.txt +135 -0
  3. data/Gemfile +5 -0
  4. data/LICENSE +22 -0
  5. data/README.md +33 -0
  6. data/Rakefile +129 -0
  7. data/bin/god +134 -0
  8. data/doc/god.asciidoc +1592 -0
  9. data/doc/intro.asciidoc +20 -0
  10. data/ext/god/.gitignore +5 -0
  11. data/ext/god/extconf.rb +56 -0
  12. data/ext/god/kqueue_handler.c +133 -0
  13. data/ext/god/netlink_handler.c +182 -0
  14. data/lib/god/behavior.rb +52 -0
  15. data/lib/god/behaviors/clean_pid_file.rb +21 -0
  16. data/lib/god/behaviors/clean_unix_socket.rb +21 -0
  17. data/lib/god/behaviors/notify_when_flapping.rb +51 -0
  18. data/lib/god/cli/command.rb +268 -0
  19. data/lib/god/cli/run.rb +170 -0
  20. data/lib/god/cli/version.rb +23 -0
  21. data/lib/god/compat19.rb +33 -0
  22. data/lib/god/condition.rb +96 -0
  23. data/lib/god/conditions/always.rb +36 -0
  24. data/lib/god/conditions/complex.rb +86 -0
  25. data/lib/god/conditions/cpu_usage.rb +80 -0
  26. data/lib/god/conditions/degrading_lambda.rb +52 -0
  27. data/lib/god/conditions/disk_usage.rb +32 -0
  28. data/lib/god/conditions/file_mtime.rb +28 -0
  29. data/lib/god/conditions/file_touched.rb +44 -0
  30. data/lib/god/conditions/flapping.rb +128 -0
  31. data/lib/god/conditions/http_response_code.rb +184 -0
  32. data/lib/god/conditions/lambda.rb +25 -0
  33. data/lib/god/conditions/memory_usage.rb +82 -0
  34. data/lib/god/conditions/process_exits.rb +66 -0
  35. data/lib/god/conditions/process_running.rb +63 -0
  36. data/lib/god/conditions/socket_responding.rb +142 -0
  37. data/lib/god/conditions/tries.rb +44 -0
  38. data/lib/god/configurable.rb +57 -0
  39. data/lib/god/contact.rb +114 -0
  40. data/lib/god/contacts/airbrake.rb +44 -0
  41. data/lib/god/contacts/campfire.rb +121 -0
  42. data/lib/god/contacts/email.rb +130 -0
  43. data/lib/god/contacts/hipchat.rb +117 -0
  44. data/lib/god/contacts/jabber.rb +75 -0
  45. data/lib/god/contacts/prowl.rb +57 -0
  46. data/lib/god/contacts/scout.rb +55 -0
  47. data/lib/god/contacts/sensu.rb +59 -0
  48. data/lib/god/contacts/slack.rb +98 -0
  49. data/lib/god/contacts/statsd.rb +46 -0
  50. data/lib/god/contacts/twitter.rb +51 -0
  51. data/lib/god/contacts/webhook.rb +74 -0
  52. data/lib/god/driver.rb +238 -0
  53. data/lib/god/errors.rb +24 -0
  54. data/lib/god/event_handler.rb +112 -0
  55. data/lib/god/event_handlers/dummy_handler.rb +13 -0
  56. data/lib/god/event_handlers/kqueue_handler.rb +17 -0
  57. data/lib/god/event_handlers/netlink_handler.rb +13 -0
  58. data/lib/god/logger.rb +109 -0
  59. data/lib/god/metric.rb +87 -0
  60. data/lib/god/process.rb +381 -0
  61. data/lib/god/registry.rb +32 -0
  62. data/lib/god/simple_logger.rb +59 -0
  63. data/lib/god/socket.rb +113 -0
  64. data/lib/god/sugar.rb +62 -0
  65. data/lib/god/sys_logger.rb +45 -0
  66. data/lib/god/system/portable_poller.rb +42 -0
  67. data/lib/god/system/process.rb +50 -0
  68. data/lib/god/system/slash_proc_poller.rb +92 -0
  69. data/lib/god/task.rb +552 -0
  70. data/lib/god/timeline.rb +25 -0
  71. data/lib/god/trigger.rb +43 -0
  72. data/lib/god/version.rb +4 -0
  73. data/lib/god/watch.rb +340 -0
  74. data/lib/god.rb +777 -0
  75. data/test/configs/child_events/child_events.god +44 -0
  76. data/test/configs/child_events/simple_server.rb +3 -0
  77. data/test/configs/child_polls/child_polls.god +37 -0
  78. data/test/configs/child_polls/simple_server.rb +12 -0
  79. data/test/configs/complex/complex.god +59 -0
  80. data/test/configs/complex/simple_server.rb +3 -0
  81. data/test/configs/contact/contact.god +118 -0
  82. data/test/configs/contact/simple_server.rb +3 -0
  83. data/test/configs/daemon_events/daemon_events.god +37 -0
  84. data/test/configs/daemon_events/simple_server.rb +8 -0
  85. data/test/configs/daemon_events/simple_server_stop.rb +11 -0
  86. data/test/configs/daemon_polls/daemon_polls.god +17 -0
  87. data/test/configs/daemon_polls/simple_server.rb +6 -0
  88. data/test/configs/degrading_lambda/degrading_lambda.god +31 -0
  89. data/test/configs/degrading_lambda/tcp_server.rb +15 -0
  90. data/test/configs/keepalive/keepalive.god +9 -0
  91. data/test/configs/keepalive/keepalive.rb +12 -0
  92. data/test/configs/lifecycle/lifecycle.god +25 -0
  93. data/test/configs/matias/matias.god +50 -0
  94. data/test/configs/real.rb +59 -0
  95. data/test/configs/running_load/running_load.god +16 -0
  96. data/test/configs/stop_options/simple_server.rb +12 -0
  97. data/test/configs/stop_options/stop_options.god +39 -0
  98. data/test/configs/stress/simple_server.rb +3 -0
  99. data/test/configs/stress/stress.god +15 -0
  100. data/test/configs/task/logs/.placeholder +0 -0
  101. data/test/configs/task/task.god +26 -0
  102. data/test/configs/test.rb +61 -0
  103. data/test/configs/usr1_trapper.rb +10 -0
  104. data/test/helper.rb +172 -0
  105. data/test/suite.rb +6 -0
  106. data/test/test_airbrake.rb +14 -0
  107. data/test/test_behavior.rb +18 -0
  108. data/test/test_campfire.rb +22 -0
  109. data/test/test_condition.rb +52 -0
  110. data/test/test_conditions_disk_usage.rb +50 -0
  111. data/test/test_conditions_http_response_code.rb +109 -0
  112. data/test/test_conditions_process_running.rb +40 -0
  113. data/test/test_conditions_socket_responding.rb +176 -0
  114. data/test/test_conditions_tries.rb +67 -0
  115. data/test/test_contact.rb +109 -0
  116. data/test/test_driver.rb +26 -0
  117. data/test/test_email.rb +34 -0
  118. data/test/test_event_handler.rb +82 -0
  119. data/test/test_god.rb +710 -0
  120. data/test/test_god_system.rb +201 -0
  121. data/test/test_handlers_kqueue_handler.rb +16 -0
  122. data/test/test_hipchat.rb +23 -0
  123. data/test/test_jabber.rb +29 -0
  124. data/test/test_logger.rb +55 -0
  125. data/test/test_metric.rb +74 -0
  126. data/test/test_process.rb +263 -0
  127. data/test/test_prowl.rb +15 -0
  128. data/test/test_registry.rb +15 -0
  129. data/test/test_sensu.rb +11 -0
  130. data/test/test_slack.rb +57 -0
  131. data/test/test_socket.rb +34 -0
  132. data/test/test_statsd.rb +22 -0
  133. data/test/test_sugar.rb +42 -0
  134. data/test/test_system_portable_poller.rb +17 -0
  135. data/test/test_system_process.rb +30 -0
  136. data/test/test_task.rb +246 -0
  137. data/test/test_timeline.rb +37 -0
  138. data/test/test_trigger.rb +63 -0
  139. data/test/test_watch.rb +286 -0
  140. data/test/test_webhook.rb +22 -0
  141. 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