god 0.10.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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