opticon 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.txt CHANGED
@@ -0,0 +1,13 @@
1
+ === 0.0.2 :: 2007-11-15
2
+
3
+ * A series of tests on a service can now be specified as a block to the
4
+ Service#test method.
5
+ * All failures generated inside a test block are all batched together
6
+ into one notification.
7
+ * Opticon test scripts can now be run with the "-v" switch to verbosely
8
+ display what the script is doing.
9
+ * Failure messages are now a bit more readable for non-techies.
10
+
11
+ === 0.0.1 :: 2007-02-21
12
+
13
+ * First public release.
data/Manifest.txt CHANGED
@@ -1,7 +1,6 @@
1
1
  Rakefile
2
2
  README.txt
3
3
  CHANGELOG.txt
4
- History.txt
5
4
  Manifest.txt
6
5
  README.txt
7
6
  Rakefile
@@ -17,8 +16,10 @@ lib/opticon/service.rb
17
16
  lib/opticon/tester.rb
18
17
  lib/opticon/version.rb
19
18
  setup.rb
19
+ test/opticon/email_notifier_test.rb
20
20
  test/opticon/http_test.rb
21
21
  test/opticon/mailer_test.rb
22
+ test/opticon/service_test.rb
22
23
  test/opticon/tester_test.rb
23
24
  test/opticon_test.rb
24
- test/test_helper.rb
25
+ test/test_helper.rb
data/README.txt CHANGED
@@ -1,4 +1,4 @@
1
- Opticon is a no-nonsense utility for monitoring HTTP-based services (websites).
1
+ Opticon is a no-nonsense utility for monitoring HTTP-based services (i.e. websites).
2
2
 
3
3
  When triggered, opticon connects to a list of URIs and checks to make sure that
4
4
  they pass some arbitrary test, such as responding with a non-error HTTP response
data/examples/sample.rb CHANGED
@@ -1,24 +1,38 @@
1
1
  #!/usr/bin/env ruby
2
- require 'rubygems'; gem 'opticon'; require 'opticon'
2
+ require 'rubygems'
3
3
 
4
- # Where notifications will be emailed
5
- SEND_TO = "sample@nowhere.foo"
4
+ begin
5
+ gem 'opticon'
6
+ rescue Gem::LoadError
7
+ # try to load local copy if gem is not available
8
+ $: << 'lib'
9
+ end
10
+
11
+ require 'opticon'
6
12
 
13
+ # SEND_TO determines where notifications will be emailed.
14
+ #
7
15
  # If you want to send to multiple mailboxes, you can specify an array of
8
16
  # addresses like so:
9
17
  #
10
18
  # SEND_TO = ['robert@nowhere.foo', 'sally@nowhere.foo']
11
19
 
12
- # Email notifications will show up as coming from this address.
13
- FROM = "opticon@nowhere.foo"
20
+ SEND_TO = "sample@nowhere.foo"
14
21
 
22
+
23
+ # Email notifications will show up as coming from the FROM address.
24
+ #
15
25
  # You may have to set the FROM value to be a real email address (e.g. your
16
26
  # email address). Otherwise your mail server may decide to drop Opticon's
17
27
  # notification messages.
18
28
 
29
+ FROM = "opticon@nowhere.foo"
30
+
31
+
19
32
  # You don't have to change anything on this next line. This configures the
20
33
  # default notification behaviour for Opticon, which is to email failures to the
21
34
  # addresses you configured above.
35
+
22
36
  Opticon.default_notifiers = Opticon::Notifier::Email.new(SEND_TO, :from => FROM)
23
37
 
24
38
  # You may need to configure the Mailer with your mail server info. Here's how to
@@ -34,6 +48,7 @@ Opticon.default_notifiers = Opticon::Notifier::Email.new(SEND_TO, :from => FROM)
34
48
  # See http://api.rubyonrails.com/classes/ActionMailer/Base.html for more details
35
49
  # on setting up the mailer.
36
50
 
51
+
37
52
  # Now you can set up some tests. Note that this is just plain Ruby code, formatted
38
53
  # a bit strangely (note the dot at the end of each line -- we are calling the test()
39
54
  # method on the URI string, and then on the result of each successive test).
@@ -47,11 +62,22 @@ Opticon.default_notifiers = Opticon::Notifier::Email.new(SEND_TO, :from => FROM)
47
62
 
48
63
  "http://google.com".
49
64
  # Google will try to redirect to http://www.google.com/
50
- test(:responds_with_code, 302)
65
+ test(:responds_with_code, 301)
51
66
  # Note that currently there is no test for checking where you are being
52
67
  # redirected to. This functionality will be added in the future.
53
68
 
54
69
 
70
+ # Alternatively, you can pack your tests into a a block (this is now the
71
+ # preferred method):
72
+
73
+ "http://www.yahoo.com/".test do
74
+ # check that the HTTP response code is in the 200 range
75
+ test(:responds_with_code, :success)
76
+ # check that the page includes the string "Privacy Policy"
77
+ test(:response_body_contains, "Privacy Policy")
78
+ end
79
+
80
+
55
81
  # For a comprehensive guide on configuring and using Opticon, please see:
56
82
  #
57
83
  # http://code.google.com/p/opticon/wiki/ConfiguringOpticon
@@ -23,7 +23,7 @@ module Opticon
23
23
 
24
24
  class ResponseCodeTestFailure < Base
25
25
  def failure_message
26
- "Expected response code #{condition.inspect} but got '#{response.message}' (#{response.code})"
26
+ "The service did not respond as expected; expected response code was #{condition.inspect} but got #{response.code.inspect} (#{response.message.inspect})"
27
27
  end
28
28
  end
29
29
 
@@ -32,7 +32,7 @@ module Opticon
32
32
  if exception
33
33
  return exception.message
34
34
  else
35
- "Content did not include the expected #{condition.class} #{condition.inspect}"
35
+ "Page content did not include the expected #{condition.class} #{condition.inspect}"
36
36
  end
37
37
  end
38
38
  end
@@ -6,15 +6,26 @@ require 'action_mailer'
6
6
  module Opticon
7
7
  class Mailer < ::ActionMailer::Base
8
8
  # we use sendmail by default since it might just work out of the box
9
- # (:smtp, which is the normal default, definetly won't work without manual configuration)
9
+ # (:smtp which is the normal default definitely won't work without manual configuration)
10
10
  self.delivery_method = :sendmail
11
11
  self.template_root = File.dirname(File.expand_path(__FILE__))+'/..'
12
12
 
13
- def failure_notification(failure, recipients, from)
13
+ def failure_notification(service, message, recipients, from)
14
+ subject = "HTTP Service Failure: #{service}"
15
+
16
+ if ARGV.include?("-v")
17
+ puts "Mailing notification:"
18
+ puts " TO: #{recipients.inspect}"
19
+ puts " SUBJECT: #{subject}"
20
+ puts " FROM: #{from}"
21
+ puts " SERVICE: #{service}"
22
+ puts " MESSAGE: #{message}"
23
+ end
24
+
14
25
  self.recipients recipients
15
26
  self.from from
16
- subject "HTTP Service Failure: #{failure.uri}"
17
- body :failure => failure
27
+ self.subject subject
28
+ self.body :service => service, :message => message
18
29
  end
19
30
  end
20
31
  end
@@ -1,5 +1,5 @@
1
- Service at '<%= @failure.uri %>' failed:
1
+ Service at '<%= @service %>' failed one or more Opticon tests:
2
2
 
3
- <%= @failure.failure_message %>
3
+ <%= @message %>
4
4
 
5
5
  (Generated by Opticon on host '<%= ENV['HOSTNAME'] %>' at <%= Time.now %>)
@@ -1,17 +1,70 @@
1
1
  require 'opticon/mailer'
2
+ require 'pstore'
2
3
 
3
4
  module Opticon
4
- module Notifier
5
+ module Notifier
6
+ # Send failure notifications via email to the given list of recipients.
7
+ #
8
+ # To set options, configure Opticon::Mailer the same way you
9
+ # would any other ActionMailer. For example, to send via SMTP:
10
+ #
11
+ # Opticon::Mailer.delivery_method = :smtp
12
+ #
13
+ # Opticon::Mailer.server_settings = {
14
+ # :address => "mail.nowhere.foo",
15
+ # :domain => "nowhere.foo",
16
+ # :authentication => :login,
17
+ # :user_name => "mail_user",
18
+ # :password => "topsecret"
19
+ # }
20
+ #
5
21
  class Email
6
22
  attr_accessor :recipients, :from
7
23
 
24
+ # The same notification will not be resent over and over again unless
25
+ # this is set to true.
26
+ @@resend = false
27
+
8
28
  def initialize(recipients, options = {:from => "opticon@#{ENV['HOSTNAME']}"})
9
29
  @recipients = recipients
10
30
  @from = options[:from]
11
31
  end
12
32
 
13
- def notify(failure)
14
- Opticon::Mailer.deliver_failure_notification(failure, recipients, from)
33
+ def notify(failures)
34
+ failures = [failures] unless failures.kind_of? Array
35
+
36
+ failures_by_uri = {}
37
+ failures.each do |f|
38
+ failures_by_uri[f.uri] ||= []
39
+ failures_by_uri[f.uri] << f
40
+ end
41
+
42
+ if ARGV.include?("-v")
43
+ puts "Notifying #{recipients.inspect} about #{failures.size} failures:"
44
+ failures.each{|f| puts " #{f.failure_message}"}
45
+ end
46
+
47
+ failures_by_uri.each do |uri, failures|
48
+ Opticon::Mailer.deliver_failure_notification(
49
+ uri,
50
+ failures.collect{|f| f.failure_message}.join("\n"),
51
+ recipients, from
52
+ )
53
+ end
54
+ end
55
+ end
56
+
57
+ # Dummy notifier used in testing. It doesn't do anything other than store
58
+ # the error_messages given to notify in the object's notifications attribute.
59
+ class Dummy
60
+ attr_accessor :notifications
61
+
62
+ def initialize()
63
+ @notifications = []
64
+ end
65
+
66
+ def notify(failures)
67
+ @notifications << failures
15
68
  end
16
69
  end
17
70
  end
@@ -21,47 +21,82 @@ module Opticon
21
21
  @uri.to_s
22
22
  end
23
23
 
24
- def test(tester, *args)
25
- case tester
26
- when :responds_with_code, 'responds_with_code'
27
- tester = Opticon::Tester::ResponseCodeTester.new
28
- when :response_body_contains, 'response_body_contains'
29
- tester = Opticon::Tester::ContentTester.new
30
- when Class
31
- tester = tester.new
32
- when Tester::Base
33
- tester = tester
24
+ def test(*args, &block)
25
+ if block
26
+ @prev_batch_notify = @batch_notify
27
+ @batch_notify = true
28
+ instance_eval(&block)
29
+ send_batch_notification
30
+ @batch_notify = @prev_batch_notify
34
31
  else
35
- begin
36
- tester_class = "Opitcon::Tester::#{tester.to_s}".constantize
37
- tester = tester_class.new
38
- rescue NameError
39
- bad_tester = true
32
+ tester_type = args[0]
33
+ condition = args[1]
34
+ options = args[2]
35
+
36
+ case tester_type
37
+ when :responds_with_code, 'responds_with_code'
38
+ tester = Opticon::Tester::ResponseCodeTester.new
39
+ when :response_body_contains, 'response_body_contains'
40
+ tester = Opticon::Tester::ContentTester.new
41
+ when Class
42
+ tester = tester.new
43
+ when Tester::Base
44
+ tester = tester
45
+ else
46
+ begin
47
+ tester_class = "Opitcon::Tester::#{tester.to_s}".constantize
48
+ tester = tester_class.new
49
+ rescue NameError
50
+ bad_tester = true
51
+ end
52
+
53
+ if bad_tester or !tester.respond_to? :test
54
+ raise ArgumentError, "'#{tester}' is not a valid tester. The parameter must be: 'responds_with_code', 'response_body_contains', or"+
55
+ " an Object or Class that responds to a 'test' method."
56
+ end
40
57
  end
41
58
 
42
- if bad_tester or !tester.respond_to? :test
43
- raise ArgumentError, "'#{tester}' is not a valid tester. The parameter must be: 'responds_with_code', 'response_body_contains', or"+
44
- " an Object or Class that responds to a 'test' method."
59
+ tester.uri = @uri
60
+
61
+ @failures ||= []
62
+
63
+ debug = "#{@uri} #{tester_type} #{condition.inspect}?"
64
+
65
+ if tester.test(condition)
66
+ puts "#{debug} ==> PASSED!" if ARGV.include?("-v")
67
+ else
68
+ puts "#{debug} ==> FAILED!" if ARGV.include?("-v")
69
+ notifiers.each {|n| n.notify(tester.failure)} unless @batch_notify
70
+ @failures << tester.failure
45
71
  end
72
+
73
+ # return self to allow for chaining test calls
74
+ return self
46
75
  end
47
-
48
- tester.uri = @uri
49
-
50
- @failures = []
51
- unless tester.test(*args)
52
- notifiers.each {|n| n.notify(tester.failure)}
53
- @failures << tester.failure
76
+ end
77
+
78
+ def send_batch_notification
79
+ notifiers.each {|n| n.notify(@failures)} unless @failures.empty?
80
+ end
81
+
82
+ def failures?
83
+ if @failures.nil?
84
+ raise "Test has not yet been run."
85
+ else
86
+ not @failures.empty?
54
87
  end
55
-
56
- # return self to allow for test chaining
57
- return self
58
88
  end
59
89
  end
60
90
  end
61
91
 
62
- # this makes it possible to treat strings as Service objects
92
+ # this makes it possible to use Strings like Service objects
63
93
  class String
64
- def test(tester, *args)
65
- Opticon::Service.new(to_s).test(tester, *args)
94
+ def method_missing(method, *args, &block)
95
+ service.send(method, *args, &block)
96
+ end
97
+
98
+ private
99
+ def service
100
+ @service ||= Opticon::Service.new(to_s)
66
101
  end
67
102
  end
@@ -9,19 +9,22 @@ module Opticon
9
9
 
10
10
  include Opticon::HTTP
11
11
 
12
- def test(*args)
12
+ # Wrapper for calling the test method.
13
+ # Generally you'll want to invoke this instead of invoking test directly.
14
+ def run(condition)
13
15
  begin
14
- test_without_exception_handling(*args)
16
+ test(condition)
15
17
  rescue SocketError, TimeoutError, Net::HTTPError, Errno::ECONNREFUSED
16
- @failure = Opticon::Failure::ConnectionFailure.new(uri, args, nil)
18
+ @failure = Opticon::Failure::ConnectionFailure.new(uri, condition, nil)
17
19
  @failure.exception = $!
18
20
  false
19
21
  end
20
22
  end
21
23
  end
22
-
24
+
25
+ # Tests that the service responds with some given HTTP status code.
23
26
  class ResponseCodeTester < Base
24
- def test_without_exception_handling(condition)
27
+ def test(condition)
25
28
  case condition
26
29
  when :ok
27
30
  responds_with_code(200)
@@ -79,8 +82,10 @@ module Opticon
79
82
  end
80
83
  end
81
84
 
85
+ # Tests that the service responds with content that matches some given
86
+ # string or regular expression.
82
87
  class ContentTester < Base
83
- def test_without_exception_handling(condition)
88
+ def test(condition)
84
89
  unless (200..206).include?(response.code.to_i)
85
90
  @failure = Opticon::Failure::ContentTestFailure.new(uri, condition, response)
86
91
  if (300..307).include?(response.code.to_i)
@@ -2,7 +2,7 @@ module Opticon #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- TINY = 1
5
+ TINY = 2
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -0,0 +1,8 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+
3
+ class Opticon::ServiceTest < Test::Unit::TestCase
4
+
5
+ def test
6
+ end
7
+
8
+ end
@@ -17,7 +17,7 @@ class Opticon::HTTPTest < Test::Unit::TestCase
17
17
  end
18
18
 
19
19
  def test_failure_notification
20
- email = Opticon::Mailer.create_failure_notification(@failure, "test@test.ruby", "opticon@test.ruby")
20
+ email = Opticon::Mailer.create_failure_notification(@failure.uri, @failure.failure_message, "test@test.ruby", "opticon@test.ruby")
21
21
  assert_match(@failure.uri, email.subject)
22
22
  assert_equal("test@test.ruby", email.to.first)
23
23
  assert_match(@failure.uri, email.body)
@@ -0,0 +1,62 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+
3
+ class Opticon::ServiceTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ Opticon.default_notifiers = Opticon::Notifier::Dummy.new
7
+ end
8
+
9
+ def test_initialize
10
+ uri = "http://www.yahoo.com/"
11
+ s = Opticon::Service.new(uri)
12
+
13
+ assert_equal Opticon.default_notifiers, s.notifiers
14
+ assert_equal uri, s.to_s
15
+ end
16
+
17
+ def test_test
18
+ uri = "http://www.yahoo.com/"
19
+ s = Opticon::Service.new(uri)
20
+
21
+ assert !s.test(:responds_with_code, 200).failures?
22
+ assert s.test(:responds_with_code, 400).failures?
23
+ assert_equal 1, s.notifiers.first.notifications.size
24
+ assert_kind_of Opticon::Failure::ResponseCodeTestFailure, s.notifiers.first.notifications.first
25
+ end
26
+
27
+ def test_string_convenience
28
+ uri = "http://www.yahoo.com/"
29
+
30
+ assert !uri.test(:responds_with_code, 200).failures?
31
+ assert uri.test(:responds_with_code, 400).failures?
32
+ end
33
+
34
+ def test_test_block
35
+ uri = "http://www.yahoo.com/"
36
+ s = Opticon::Service.new(uri)
37
+
38
+ s.test do
39
+ test(:responds_with_code, 200)
40
+ test(:responds_with_code, 400)
41
+ end
42
+
43
+ assert s.failures?
44
+ assert_equal 1, s.notifiers.first.notifications.size
45
+ assert_kind_of Opticon::Failure::ResponseCodeTestFailure, s.notifiers.first.notifications.first.first
46
+ end
47
+
48
+ def test_test_block_on_string
49
+ uri = "http://www.yahoo.com/"
50
+
51
+ uri.test do
52
+ test(:responds_with_code, 200)
53
+ test(:responds_with_code, 400)
54
+ end
55
+
56
+ assert uri.failures?
57
+ assert_equal 1, uri.notifiers.first.notifications.size
58
+
59
+ assert_kind_of Opticon::Failure::ResponseCodeTestFailure, uri.notifiers.first.notifications.first.first
60
+ end
61
+
62
+ end
@@ -9,18 +9,18 @@ class Opticon::HTTPTest < Test::Unit::TestCase
9
9
  tester = Opticon::Tester::ResponseCodeTester.new
10
10
  tester.uri = "http://www.yahoo.com"
11
11
 
12
- assert tester.test(:success)
13
- assert tester.test(200)
14
- assert tester.test([200, 302])
15
- assert !tester.test(:server_error)
12
+ assert tester.run(:success)
13
+ assert tester.run(200)
14
+ assert tester.run([200, 302])
15
+ assert !tester.run(:server_error)
16
16
  end
17
17
 
18
18
  def test_response_code_tester_failure
19
19
  tester = Opticon::Tester::ResponseCodeTester.new
20
20
  tester.uri = "http://www.yahoo.com/alskjdflaksjflkasjfklsTHIS_PAGE_DOESNT_EXIST"
21
21
 
22
- assert tester.test(404)
23
- assert !tester.test(:success)
22
+ assert tester.run(404)
23
+ assert !tester.run(:success)
24
24
  assert_kind_of Opticon::Failure::ResponseCodeTestFailure, tester.failure
25
25
  assert_equal 404.to_s, tester.failure.response.code.to_s
26
26
  end
@@ -29,13 +29,13 @@ class Opticon::HTTPTest < Test::Unit::TestCase
29
29
  tester = Opticon::Tester::ResponseCodeTester.new
30
30
  tester.uri = "http://there.is.no.such.server.foobarblah/test"
31
31
 
32
- assert !tester.test(:success)
32
+ assert !tester.run(:success)
33
33
  assert_kind_of Opticon::Failure::ConnectionFailure, tester.failure
34
34
 
35
35
  tester = Opticon::Tester::ResponseCodeTester.new
36
36
  tester.uri = "http://localhost:65489789"
37
37
 
38
- assert !tester.test(:success)
38
+ assert !tester.run(:success)
39
39
  assert_kind_of Opticon::Failure::ConnectionFailure, tester.failure
40
40
  end
41
41
 
@@ -43,11 +43,11 @@ class Opticon::HTTPTest < Test::Unit::TestCase
43
43
  tester = Opticon::Tester::ContentTester.new
44
44
 
45
45
  tester.uri = "http://code.google.com/p/opticon/"
46
- assert tester.test("matt.zukowski"), tester.failure
46
+ assert tester.run("matt.zukowski"), tester.failure
47
47
  assert_nil tester.failure
48
48
 
49
49
  bad_string = "THIS STRING DOESNT EXIST IN OPTICON's GOOGLE CODE PAGE!!!"
50
- assert !tester.test(bad_string), tester.failure
50
+ assert !tester.run(bad_string), tester.failure
51
51
  assert_equal bad_string, tester.failure.condition
52
52
  end
53
53
 
@@ -55,11 +55,11 @@ class Opticon::HTTPTest < Test::Unit::TestCase
55
55
  tester = Opticon::Tester::ContentTester.new
56
56
 
57
57
  tester.uri = "http://code.google.com/p/opticon/"
58
- assert tester.test(/gnu general public license [23]\.[0-9]/i), tester.failure
58
+ assert tester.run(/gnu general public license v[0-9]/i), tester.failure
59
59
  assert_nil tester.failure
60
60
 
61
61
  bad_regexp = /klasjfkljasdlkfjasklfjslkdfj/
62
- assert !tester.test(bad_regexp), tester.failure
62
+ assert !tester.run(bad_regexp), tester.failure
63
63
  assert_equal bad_regexp, tester.failure.condition
64
64
  end
65
65
 
@@ -68,10 +68,9 @@ class Opticon::HTTPTest < Test::Unit::TestCase
68
68
 
69
69
  tester.uri = "http://google.com/"
70
70
  # google will try to redirect to http://www.google.com/
71
- assert_equal 302, tester.response.code.to_i
72
- assert !tester.test("Google Search"), tester.failure
71
+ assert_equal 301, tester.response.code.to_i
72
+ assert !tester.run("Google Search"), tester.failure
73
73
  assert_equal "http://www.google.com/", tester.failure.response['location']
74
74
  end
75
75
 
76
-
77
76
  end
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.0
2
+ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: opticon
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.1
7
- date: 2007-02-20 00:00:00 -05:00
6
+ version: 0.0.2
7
+ date: 2007-11-15 00:00:00 -05:00
8
8
  summary: Peace of mind through automated monitoring of your HTTP services.
9
9
  require_paths:
10
10
  - lib
@@ -32,7 +32,6 @@ files:
32
32
  - Rakefile
33
33
  - README.txt
34
34
  - CHANGELOG.txt
35
- - History.txt
36
35
  - Manifest.txt
37
36
  - examples/sample.rb
38
37
  - lib/opticon.rb
@@ -46,15 +45,19 @@ files:
46
45
  - lib/opticon/tester.rb
47
46
  - lib/opticon/version.rb
48
47
  - setup.rb
48
+ - test/opticon/email_notifier_test.rb
49
49
  - test/opticon/http_test.rb
50
50
  - test/opticon/mailer_test.rb
51
+ - test/opticon/service_test.rb
51
52
  - test/opticon/tester_test.rb
52
53
  - test/opticon_test.rb
53
54
  - test/test_helper.rb
54
55
  test_files:
55
56
  - test/opticon_test.rb
57
+ - test/opticon/service_test.rb
56
58
  - test/opticon/tester_test.rb
57
59
  - test/opticon/mailer_test.rb
60
+ - test/opticon/email_notifier_test.rb
58
61
  - test/opticon/http_test.rb
59
62
  rdoc_options: []
60
63
 
data/History.txt DELETED
File without changes