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.
@@ -1,4 +1,12 @@
1
- ==
1
+ == 0.11.0 / 2010-07-01
2
+ * Major Changes
3
+ * Rewrite notification system to be more consistent and flexible.
4
+
5
+ == 0.10.1 / 2010-05-17
6
+ * Bug Fixes
7
+ * Fix date in gemspec
8
+
9
+ == 0.10.0 / 2010-05-17
2
10
  * Minor Enhancements
3
11
  * Add stop_timeout and stop_signal options to Watch
4
12
  * Bug Fixes
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
3
3
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
4
 
5
5
  s.name = 'god'
6
- s.version = '0.10.1'
7
- s.date = '2010-05-17'
6
+ s.version = '0.11.0'
7
+ s.date = '2010-07-01'
8
8
 
9
9
  s.summary = "Process monitoring framework."
10
10
  s.description = "An easy to configure, easy to extend monitoring framework written in Ruby."
@@ -26,7 +26,6 @@ Gem::Specification.new do |s|
26
26
 
27
27
  s.add_development_dependency('twitter', [">= 0.3.7"])
28
28
  s.add_development_dependency('prowly', [">= 0.2.1"])
29
- s.add_development_dependency('tinder', [">= 1.3.1", "< 2.0.0"])
30
29
  s.add_development_dependency('xmpp4r', [">= 0.4.0"])
31
30
  s.add_development_dependency('dike', [">= 0.0.3"])
32
31
  s.add_development_dependency('snapshot', [">= 1.0.0", "< 2.0.0"])
data/lib/god.rb CHANGED
@@ -48,27 +48,6 @@ require 'god/conditions/disk_usage'
48
48
  require 'god/conditions/complex'
49
49
  require 'god/conditions/file_mtime'
50
50
 
51
- require 'god/contact'
52
- require 'god/contacts/email'
53
- require 'god/contacts/webhook'
54
- begin
55
- require 'god/contacts/twitter'
56
- rescue LoadError
57
- end
58
- begin
59
- require 'god/contacts/jabber'
60
- rescue LoadError
61
- end
62
- begin
63
- require 'god/contacts/campfire'
64
- rescue LoadError
65
- end
66
- begin
67
- require 'god/contacts/prowl'
68
- rescue LoadError
69
- end
70
-
71
-
72
51
  require 'god/socket'
73
52
  require 'god/driver'
74
53
 
@@ -87,6 +66,25 @@ require 'god/cli/command'
87
66
 
88
67
  require 'god/diagnostics'
89
68
 
69
+ CONTACT_DEPS = { }
70
+ CONTACT_LOAD_SUCCESS = { }
71
+
72
+ def load_contact(name)
73
+ require "god/contacts/#{name}"
74
+ CONTACT_LOAD_SUCCESS[name] = true
75
+ rescue LoadError
76
+ CONTACT_LOAD_SUCCESS[name] = false
77
+ end
78
+
79
+ require 'god/contact'
80
+ load_contact(:campfire)
81
+ load_contact(:email)
82
+ load_contact(:jabber)
83
+ load_contact(:prowl)
84
+ load_contact(:scout)
85
+ load_contact(:twitter)
86
+ load_contact(:webhook)
87
+
90
88
  $:.unshift File.join(File.dirname(__FILE__), *%w[.. ext god])
91
89
 
92
90
  # App wide logging system
@@ -150,7 +148,7 @@ class Module
150
148
  end
151
149
 
152
150
  module God
153
- VERSION = '0.10.1'
151
+ VERSION = '0.11.0'
154
152
  LOG_BUFFER_SIZE_DEFAULT = 100
155
153
  PID_FILE_DIRECTORY_DEFAULTS = ['/var/run/god', '~/.god/pids']
156
154
  DRB_PORT_DEFAULT = 17165
@@ -352,6 +350,16 @@ module God
352
350
  def self.contact(kind)
353
351
  self.internal_init
354
352
 
353
+ # verify contact has been loaded
354
+ if CONTACT_LOAD_SUCCESS[kind] == false
355
+ applog(nil, :error, "A required dependency for the #{kind} contact is unavailable.")
356
+ applog(nil, :error, "Run the following commands to install the dependencies:")
357
+ CONTACT_DEPS[kind].each do |d|
358
+ applog(nil, :error, " [sudo] gem install #{d}")
359
+ end
360
+ abort
361
+ end
362
+
355
363
  # create the contact
356
364
  begin
357
365
  c = Contact.generate(kind)
@@ -24,6 +24,14 @@ module God
24
24
  valid
25
25
  end
26
26
 
27
+ def self.defaults
28
+ yield self
29
+ end
30
+
31
+ def arg(name)
32
+ self.instance_variable_get("@#{name}") || self.class.instance_variable_get("@#{name}")
33
+ end
34
+
27
35
  # Normalize the given notify specification into canonical form.
28
36
  # +spec+ is the notify spec as a String, Array of Strings, or Hash
29
37
  #
@@ -1,81 +1,119 @@
1
- # notify campfire using tinder http://tinder.rubyforge.org
1
+ # Send a notice to a Campfire room (http://campfirenow.com).
2
2
  #
3
- # Example: set up a new campfire notifier
4
- #
5
- # Credentials
6
- #
7
- # God::Contacts::Campfire.server_settings = {
8
- # :subdomain => "yoursubdomain",
9
- # :user_name => "youruser",
10
- # :room => "yourroom",
11
- # :password => "yourpassword"
12
- # }
13
- #
14
- # Register a new notifier
15
- #
16
- # God.contact(:campfire) do |c|
17
- # c.name = 'campfire'
18
- # end
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 = 'campfire'
26
- # end
27
- # end
3
+ # subdomain - The String subdomain of the Campfire account. If your URL is
4
+ # "foo.campfirenow.com" then your subdomain is "foo".
5
+ # token - The String token used for authentication.
6
+ # room - The String room name to which the message should be sent.
7
+ # ssl - A Boolean determining whether or not to use SSL
8
+ # (default: false).
9
+
10
+ require 'net/http'
11
+ require 'net/https'
12
+
13
+ CONTACT_DEPS[:campfire] = ['json']
14
+ CONTACT_DEPS[:campfire].each do |d|
15
+ require d
16
+ end
17
+
18
+ module Marshmallow
19
+ class Connection
20
+ def initialize(options)
21
+ raise "Required option :subdomain not set." unless options[:subdomain]
22
+ raise "Required option :token not set." unless options[:token]
23
+ @options = options
24
+ end
25
+
26
+ def base_url
27
+ scheme = @options[:ssl] ? 'https' : 'http'
28
+ subdomain = @options[:subdomain]
29
+ "#{scheme}://#{subdomain}.campfirenow.com"
30
+ end
31
+
32
+ def find_room_id_by_name(room)
33
+ url = URI.parse("#{base_url}/rooms.json")
34
+
35
+ http = Net::HTTP.new(url.host, url.port)
36
+ http.use_ssl = true if @options[:ssl]
37
+
38
+ req = Net::HTTP::Get.new(url.path)
39
+ req.basic_auth(@options[:token], 'X')
28
40
 
29
- require 'tinder'
41
+ res = http.request(req)
42
+ case res
43
+ when Net::HTTPSuccess
44
+ rooms = JSON.parse(res.body)
45
+ room = rooms['rooms'].select { |x| x['name'] == room }
46
+ rooms.empty? ? nil : room.first['id']
47
+ else
48
+ raise res.error!
49
+ end
50
+ end
51
+
52
+ def speak(room, message)
53
+ room_id = find_room_id_by_name(room)
54
+ raise "No such room: #{room}." unless room_id
55
+
56
+ url = URI.parse("#{base_url}/room/#{room_id}/speak.json")
57
+
58
+ http = Net::HTTP.new(url.host, url.port)
59
+ http.use_ssl = true if @options[:ssl]
60
+
61
+ req = Net::HTTP::Post.new(url.path)
62
+ req.basic_auth(@options[:token], 'X')
63
+ req.set_content_type('application/json')
64
+ req.body = { 'message' => { 'body' => message } }.to_json
65
+
66
+ res = http.request(req)
67
+ case res
68
+ when Net::HTTPSuccess
69
+ true
70
+ else
71
+ raise res.error!
72
+ end
73
+ end
74
+ end
75
+ end
30
76
 
31
77
  module God
32
78
  module Contacts
33
79
 
34
80
  class Campfire < Contact
35
81
  class << self
36
- attr_accessor :server_settings, :format
82
+ attr_accessor :subdomain, :token, :room, :ssl
83
+ attr_accessor :format
37
84
  end
38
85
 
39
- self.server_settings = {:subdomain => '',
40
- :user_name => '',
41
- :password => '',
42
- :room => '',
43
- :ssl => false}
86
+ self.ssl = false
44
87
 
45
- self.format = lambda do |message, host|
46
- <<-EOF
47
- #{host} - #{message}
48
- EOF
88
+ self.format = lambda do |message, time, priority, category, host|
89
+ "[#{time.strftime('%H:%M:%S')}] #{host} - #{message}"
49
90
  end
50
91
 
51
- def initialize
52
- @room = nil
92
+ attr_accessor :subdomain, :token, :room, :ssl
93
+
94
+ def valid?
95
+ valid = true
96
+ valid &= complain("Attribute 'subdomain' must be specified", self) unless arg(:subdomain)
97
+ valid &= complain("Attribute 'token' must be specified", self) unless arg(:token)
98
+ valid &= complain("Attribute 'room' must be specified", self) unless arg(:room)
99
+ valid
53
100
  end
54
101
 
55
102
  def notify(message, time, priority, category, host)
56
- begin
57
- body = Campfire.format.call(message,host)
58
-
59
- room.speak body
60
-
61
- self.info = "notified campfire: #{Campfire.server_settings[:subdomain]}"
62
- rescue => e
63
- applog(nil, :info, "failed to notify campfire: #{e.message}")
64
- applog(nil, :debug, e.backtrace.join("\n"))
65
- end
66
- end
103
+ body = Campfire.format.call(message, time, priority, category, host)
67
104
 
68
- private
105
+ conn = Marshmallow::Connection.new(
106
+ :subdomain => arg(:subdomain),
107
+ :token => arg(:token),
108
+ :ssl => arg(:ssl)
109
+ )
69
110
 
70
- def room
71
- unless @room
72
- applog(nil,:debug, "initializing campfire connection using credentials: #{Campfire.server_settings.inspect}")
111
+ conn.speak(arg(:room), body)
73
112
 
74
- campfire = Tinder::Campfire.new Campfire.server_settings[:subdomain], :ssl => Campfire.server_settings[:ssl]
75
- campfire.login Campfire.server_settings[:user_name], Campfire.server_settings[:password]
76
- @room = campfire.find_room_by_name(Campfire.server_settings[:room])
77
- end
78
- @room
113
+ self.info = "notified campfire: #{arg(:subdomain)}"
114
+ rescue Object => e
115
+ applog(nil, :info, "failed to notify campfire: #{e.message}")
116
+ applog(nil, :debug, e.backtrace.join("\n"))
79
117
  end
80
118
  end
81
119
 
@@ -1,36 +1,59 @@
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 Boolean of whether or not to use authentication
14
+ # (default: false).
15
+ #
16
+ # === SMTP Auth Options (when server_auth = true) ===
17
+ # server_domain - The String domain.
18
+ # server_user - The String username.
19
+ # server_password - The String password.
20
+ #
21
+ # === Sendmail Options (when delivery_method = :sendmail) ===
22
+ # sendmail_path - The String path to the sendmail executable
23
+ # (default: "/usr/sbin/sendmail").
24
+ # sendmail_args - The String args to send to sendmail (default "-i -t").
25
+
1
26
  require 'time'
2
27
  require 'net/smtp'
3
28
 
4
29
  module God
5
30
  module Contacts
6
-
31
+
7
32
  class Email < Contact
8
33
  class << self
9
- attr_accessor :message_settings, :delivery_method, :server_settings, :sendmail_settings, :format
34
+ attr_accessor :to_email, :to_name, :from_email, :from_name,
35
+ :delivery_method, :server_host, :server_port,
36
+ :server_auth, :server_domain, :server_user,
37
+ :server_password, :sendmail_path, :sendmail_args
38
+ attr_accessor :format
10
39
  end
11
-
12
- self.message_settings = {:from => 'god@example.com'}
13
-
14
- self.delivery_method = :smtp # or :sendmail
15
-
16
- self.server_settings = {:address => 'localhost',
17
- :port => 25}
18
- # :domain
19
- # :user_name
20
- # :password
21
- # :authentication
22
40
 
23
- self.sendmail_settings = {:location => '/usr/sbin/sendmail',
24
- :arguments => '-i -t'
25
- }
26
-
27
- self.format = lambda do |name, email, message, time, priority, category, host|
41
+ self.from_email = 'god@example.com'
42
+ self.from_name = 'God Process Monitoring'
43
+ self.delivery_method = :smtp
44
+ self.server_auth = false
45
+ self.server_host = 'localhost'
46
+ self.server_port = 25
47
+ self.sendmail_path = '/usr/sbin/sendmail'
48
+ self.sendmail_args = '-i -t'
49
+
50
+ self.format = lambda do |name, from_email, from_name, to_email, to_name, message, time, priority, category, host|
28
51
  <<-EOF
29
- From: god <#{self.message_settings[:from]}>
30
- To: #{name} <#{email}>
52
+ From: #{from_name} <#{from_email}>
53
+ To: #{to_name || name} <#{to_email}>
31
54
  Subject: [god] #{message}
32
- Date: #{Time.now.httpdate}
33
- Message-Id: <#{rand(1000000000).to_s(36)}.#{$$}.#{self.message_settings[:from]}>
55
+ Date: #{time.httpdate}
56
+ Message-Id: <#{rand(1000000000).to_s(36)}.#{$$}.#{from_email}>
34
57
 
35
58
  Message: #{message}
36
59
  Host: #{host}
@@ -38,53 +61,62 @@ Priority: #{priority}
38
61
  Category: #{category}
39
62
  EOF
40
63
  end
41
-
42
- attr_accessor :email
43
-
64
+
65
+ attr_accessor :to_email, :to_name, :from_email, :from_name,
66
+ :delivery_method, :server_host, :server_port,
67
+ :server_auth, :server_domain, :server_user,
68
+ :server_password, :sendmail_path, :sendmail_args
69
+
44
70
  def valid?
45
71
  valid = true
46
- valid &= complain("Attribute 'email' must be specified", self) if self.email.nil?
72
+ valid &= complain("Attribute 'to_email' must be specified", self) unless arg(:to_email)
73
+ valid &= complain("Attribute 'delivery_method' must be one of [ :smtp | :sendmail ]", self) unless [:smtp, :sendmail].include?(arg(:delivery_method))
74
+ if arg(:delivery_method) == :smtp
75
+ valid &= complain("Attribute 'server_host' must be specified", self) unless arg(:server_host)
76
+ valid &= complain("Attribute 'server_port' must be specified", self) unless arg(:server_port)
77
+ if arg(:server_auth)
78
+ valid &= complain("Attribute 'server_domain' must be specified", self) unless arg(:server_domain)
79
+ valid &= complain("Attribute 'server_user' must be specified", self) unless arg(:server_user)
80
+ valid &= complain("Attribute 'server_password' must be specified", self) unless arg(:server_password)
81
+ end
82
+ end
47
83
  valid
48
84
  end
49
-
85
+
50
86
  def notify(message, time, priority, category, host)
51
- begin
52
- body = Email.format.call(self.name, self.email, message, time, priority, category, host)
87
+ body = Email.format.call(self.name, arg(:from_email), arg(:from_name),
88
+ arg(:to_email), arg(:to_name), message, time,
89
+ priority, category, host)
53
90
 
54
- case Email.delivery_method
91
+ case arg(:delivery_method)
55
92
  when :smtp
56
93
  notify_smtp(body)
57
94
  when :sendmail
58
95
  notify_sendmail(body)
59
- else
60
- raise "unknown delivery method: #{Email.delivery_method}"
61
- end
62
-
63
- self.info = "sent email to #{self.email}"
64
- rescue => e
65
- applog(nil, :info, "failed to send email to #{self.email}: #{e.message}")
66
- applog(nil, :debug, e.backtrace.join("\n"))
67
96
  end
68
- end
69
97
 
70
- # private
98
+ self.info = "sent email to #{arg(:to_email)} via #{arg(:delivery_method).to_s}"
99
+ rescue Object => e
100
+ applog(nil, :info, "failed to send email to #{arg(:to_email)} via #{arg(:delivery_method).to_s}: #{e.message}")
101
+ applog(nil, :debug, e.backtrace.join("\n"))
102
+ end
71
103
 
72
104
  def notify_smtp(mail)
73
- args = [Email.server_settings[:address], Email.server_settings[:port]]
74
- if Email.server_settings[:authentication]
75
- args << Email.server_settings[:domain]
76
- args << Email.server_settings[:user_name]
77
- args << Email.server_settings[:password]
78
- args << Email.server_settings[:authentication]
105
+ args = [arg(:server_host), arg(:server_port)]
106
+ if arg(:server_auth)
107
+ args << arg(:server_domain)
108
+ args << arg(:server_user)
109
+ args << arg(:server_password)
110
+ args << arg(:server_auth)
79
111
  end
80
112
 
81
113
  Net::SMTP.start(*args) do |smtp|
82
- smtp.send_message mail, Email.message_settings[:from], self.email
114
+ smtp.send_message(mail, arg(:from_email), arg(:to_email))
83
115
  end
84
116
  end
85
117
 
86
118
  def notify_sendmail(mail)
87
- IO.popen("#{Email.sendmail_settings[:location]} #{Email.sendmail_settings[:arguments]}","w+") do |sm|
119
+ IO.popen("#{arg(:sendmail_path)} #{arg(:sendmail_args)}","w+") do |sm|
88
120
  sm.print(mail.gsub(/\r/, ''))
89
121
  sm.flush
90
122
  end