sonic 0.0.1

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 (74) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +4 -0
  3. data/Guardfile +8 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +31 -0
  6. data/Rakefile +1 -0
  7. data/app/controllers/sonic/results_controller.rb +15 -0
  8. data/app/views/sonic/results/index.html.erb +22 -0
  9. data/config/routes.rb +5 -0
  10. data/fixture_rails_root/.gitignore +15 -0
  11. data/fixture_rails_root/Gemfile +38 -0
  12. data/fixture_rails_root/README.rdoc +261 -0
  13. data/fixture_rails_root/Rakefile +7 -0
  14. data/fixture_rails_root/app/assets/images/rails.png +0 -0
  15. data/fixture_rails_root/app/assets/javascripts/application.js +15 -0
  16. data/fixture_rails_root/app/assets/stylesheets/application.css +13 -0
  17. data/fixture_rails_root/app/controllers/application_controller.rb +3 -0
  18. data/fixture_rails_root/app/helpers/application_helper.rb +2 -0
  19. data/fixture_rails_root/app/mailers/.gitkeep +0 -0
  20. data/fixture_rails_root/app/models/.gitkeep +0 -0
  21. data/fixture_rails_root/app/views/layouts/application.html.erb +14 -0
  22. data/fixture_rails_root/config.ru +4 -0
  23. data/fixture_rails_root/config/application.rb +62 -0
  24. data/fixture_rails_root/config/boot.rb +6 -0
  25. data/fixture_rails_root/config/database.yml +25 -0
  26. data/fixture_rails_root/config/environment.rb +5 -0
  27. data/fixture_rails_root/config/environments/development.rb +37 -0
  28. data/fixture_rails_root/config/environments/production.rb +67 -0
  29. data/fixture_rails_root/config/environments/test.rb +37 -0
  30. data/fixture_rails_root/config/initializers/backtrace_silencers.rb +7 -0
  31. data/fixture_rails_root/config/initializers/inflections.rb +15 -0
  32. data/fixture_rails_root/config/initializers/mime_types.rb +5 -0
  33. data/fixture_rails_root/config/initializers/secret_token.rb +7 -0
  34. data/fixture_rails_root/config/initializers/session_store.rb +8 -0
  35. data/fixture_rails_root/config/initializers/wrap_parameters.rb +14 -0
  36. data/fixture_rails_root/config/locales/en.yml +5 -0
  37. data/fixture_rails_root/config/routes.rb +58 -0
  38. data/fixture_rails_root/db/seeds.rb +7 -0
  39. data/fixture_rails_root/lib/assets/.gitkeep +0 -0
  40. data/fixture_rails_root/lib/tasks/.gitkeep +0 -0
  41. data/fixture_rails_root/log/.gitkeep +0 -0
  42. data/fixture_rails_root/public/404.html +26 -0
  43. data/fixture_rails_root/public/422.html +26 -0
  44. data/fixture_rails_root/public/500.html +25 -0
  45. data/fixture_rails_root/public/favicon.ico +0 -0
  46. data/fixture_rails_root/public/index.html +241 -0
  47. data/fixture_rails_root/public/robots.txt +5 -0
  48. data/fixture_rails_root/script/rails +6 -0
  49. data/fixture_rails_root/test/fixtures/.gitkeep +0 -0
  50. data/fixture_rails_root/test/functional/.gitkeep +0 -0
  51. data/fixture_rails_root/test/integration/.gitkeep +0 -0
  52. data/fixture_rails_root/test/performance/browsing_test.rb +12 -0
  53. data/fixture_rails_root/test/test_helper.rb +13 -0
  54. data/fixture_rails_root/test/unit/.gitkeep +0 -0
  55. data/fixture_rails_root/vendor/assets/javascripts/.gitkeep +0 -0
  56. data/fixture_rails_root/vendor/assets/stylesheets/.gitkeep +0 -0
  57. data/fixture_rails_root/vendor/plugins/.gitkeep +0 -0
  58. data/lib/generators/USAGE +9 -0
  59. data/lib/generators/sonic_generator.rb +19 -0
  60. data/lib/generators/templates/README +13 -0
  61. data/lib/generators/templates/clock.rb +36 -0
  62. data/lib/generators/templates/config.rb +15 -0
  63. data/lib/sonic.rb +22 -0
  64. data/lib/sonic/protocols/amqp.rb +32 -0
  65. data/lib/sonic/protocols/base.rb +6 -0
  66. data/lib/sonic/protocols/http.rb +32 -0
  67. data/lib/sonic/protocols/tcp.rb +58 -0
  68. data/lib/sonic/service_checker.rb +39 -0
  69. data/lib/sonic/service_checker_builder.rb +14 -0
  70. data/lib/sonic/version.rb +3 -0
  71. data/sonic.gemspec +35 -0
  72. data/spec/sonic/service_checker_spec.rb +196 -0
  73. data/spec/spec_helper.rb +28 -0
  74. metadata +332 -0
@@ -0,0 +1,15 @@
1
+ module Sonic
2
+ checks = []
3
+
4
+ # Configure your Sonic checks here
5
+
6
+ # HTTP check example
7
+ #
8
+ # checks << Sonic.service_checker do
9
+ # protocol :http
10
+ # host 'myhost'
11
+ # port 80
12
+ # end
13
+
14
+ SONIC_CHECKS = checks
15
+ end
@@ -0,0 +1,22 @@
1
+ require "docile"
2
+ require "sonic/version"
3
+ require "rails"
4
+
5
+ module Sonic
6
+ autoload :ServiceChecker, 'sonic/service_checker'
7
+ autoload :ServiceCheckerBuilder, 'sonic/service_checker_builder'
8
+ module Protocol
9
+ autoload :Base, 'sonic/protocols/base'
10
+ autoload :HTTP, 'sonic/protocols/http'
11
+ autoload :AMQP, 'sonic/protocols/amqp'
12
+ autoload :TCP, 'sonic/protocols/tcp'
13
+ end
14
+
15
+ class Engine < Rails::Engine
16
+ isolate_namespace Sonic
17
+ end
18
+
19
+ def self.service_checker(&block)
20
+ Docile.dsl_eval(Sonic::ServiceCheckerBuilder.new, &block).build
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ require 'bunny'
2
+
3
+ module Sonic
4
+ module Protocol
5
+ class AMQP
6
+ include Protocol::Base
7
+
8
+ def initialize(service_checker)
9
+ @service_checker = service_checker
10
+ end
11
+
12
+ def check
13
+ begin
14
+ conn = ::Bunny.new(:host => @service_checker.host, :port => @service_checker.port)
15
+ conn.start
16
+ @service_checker.response = conn.status
17
+ if @service_checker.response == :open
18
+ true
19
+ else
20
+ @service_checker.error = "service error"
21
+ false
22
+ end
23
+ conn.close
24
+ rescue Exception => e
25
+ @service_checker.error = e.to_s
26
+ false
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,6 @@
1
+ module Sonic
2
+ module Protocol
3
+ module Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,32 @@
1
+ require 'net/http'
2
+
3
+ module Sonic
4
+ module Protocol
5
+ class HTTP
6
+ include Protocol::Base
7
+
8
+ def initialize(service_checker)
9
+ @service_checker = service_checker
10
+ port_suffix = service_checker.port ? ":#{service_checker.port}" : ""
11
+ @uri = URI.parse("#{service_checker.protocol}://#{service_checker.host}#{port_suffix}/#{service_checker.path}")
12
+ end
13
+
14
+ def get
15
+ begin
16
+ @service_checker.response = Net::HTTP.get_response(@uri)
17
+ case @service_checker.response.code
18
+ when '200', '201', '202', '203', '204', '205', '206'
19
+ true
20
+ else
21
+ @service_checker.error = "service error"
22
+ false
23
+ end
24
+ rescue Exception => e
25
+ @service_checker.error = e.to_s
26
+ false
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,58 @@
1
+ require 'socket'
2
+
3
+ module Sonic
4
+ module Protocol
5
+ class TCP
6
+ include Protocol::Base
7
+
8
+ def initialize(service_checker)
9
+ @service_checker = service_checker
10
+ @host = service_checker.host
11
+ @port = service_checker.port
12
+ @payload = service_checker.payload
13
+ @ssl_key = service_checker.ssl_key
14
+ @ssl_cert = service_checker.ssl_cert
15
+ end
16
+
17
+ def send
18
+ begin
19
+ connection = TCPSocket.new(@host, @port)
20
+ if @ssl_cert && @ssl_key
21
+ ssl_context = OpenSSL::SSL::SSLContext.new()
22
+ ssl_context.cert = OpenSSL::X509::Certificate.new(File.read("#{Dir.pwd}/ssl/#{@ssl_cert}"))
23
+ ssl_context.ca_file = "#{Dir.pwd}/ssl/#{@ssl_cert}"
24
+ ssl_context.key = OpenSSL::PKey::RSA.new(File.read("#{Dir.pwd}/ssl/#{@ssl_key}"))
25
+ socket = OpenSSL::SSL::SSLSocket.new(connection, ssl_context)
26
+ socket.sync_close = true
27
+ socket.connect
28
+ else
29
+ socket = connection
30
+ end
31
+ socket.syswrite(@payload)
32
+ read_tcp_response(socket)
33
+ socket.close
34
+ socket = nil
35
+ if @service_checker.response
36
+ true
37
+ else
38
+ false
39
+ end
40
+ rescue Exception => e
41
+ @service_checker.error = e.to_s
42
+ false
43
+ end
44
+
45
+ end
46
+
47
+ private
48
+ def read_tcp_response(socket)
49
+ @service_checker.response = ""
50
+ @service_checker.response << socket.sysread(1)
51
+ while (@service_checker.response =~ /^.+?\n/) == nil
52
+ @service_checker.response << socket.sysread(1)
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,39 @@
1
+ module Sonic
2
+ class ServiceChecker
3
+
4
+ attr_accessor :protocol
5
+ attr_accessor :host
6
+ attr_accessor :port
7
+ attr_accessor :path
8
+ attr_accessor :payload
9
+ attr_accessor :ssl_key
10
+ attr_accessor :ssl_cert
11
+ attr_accessor :response
12
+ attr_accessor :error
13
+
14
+ def initialize(protocol, host, port, path=nil, payload=nil, ssl_cert=nil, ssl_key=nil)
15
+ @protocol = protocol
16
+ @host = host
17
+ @port = port
18
+ @path = path
19
+ @payload = payload
20
+ @ssl_key = ssl_key
21
+ @ssl_cert = ssl_cert
22
+ end
23
+
24
+ def check_service()
25
+ case protocol
26
+ when :http, :https
27
+ http = Protocol::HTTP.new(self)
28
+ http.get
29
+ when :amqp
30
+ amqp = Protocol::AMQP.new(self)
31
+ amqp.check
32
+ when :tcp
33
+ tcp = Protocol::TCP.new(self)
34
+ tcp.send
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,14 @@
1
+ module Sonic
2
+ class ServiceCheckerBuilder
3
+ def protocol(v=nil); @protocol = v; self; end
4
+ def host(v=nil); @host = v; self; end
5
+ def port(v=nil); @port = v; self; end
6
+ def path(v=nil); @path = v; self; end
7
+ def payload(v=nil); @payload = v; self; end
8
+ def ssl_cert(v=nil); @ssl_cert = v; self; end
9
+ def ssl_key(v=nil); @ssl_key = v; self; end
10
+ def build
11
+ Sonic::ServiceChecker.new(@protocol, @host, @port, @path, @payload, @ssl_cert, @ssl_key)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Sonic
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sonic/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sonic"
8
+ spec.version = Sonic::VERSION
9
+ spec.authors = ["Michael Bulat"]
10
+ spec.email = ["mbulat@crazydogsoftware.com"]
11
+ spec.description = %q{Sonic is a Rails engine which provides a status page for your applications
12
+ services, along with a number of tools to periodically check those services.}
13
+ spec.summary = %q{Sonic is a Rails Engine status page and services checker.}
14
+ spec.homepage = "http://github.com/sonic"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "rails", "~> 3.1"
23
+ spec.add_dependency "bunny", "~> 0.10"
24
+ spec.add_dependency "clockwork"
25
+ spec.add_dependency "docile"
26
+ spec.add_development_dependency "bundler", "~> 1.3"
27
+ spec.add_development_dependency "rake"
28
+ spec.add_development_dependency "sqlite3"
29
+ spec.add_development_dependency "rspec"
30
+ spec.add_development_dependency "rspec-rails", "~> 2.6"
31
+ spec.add_development_dependency "guard"
32
+ spec.add_development_dependency "guard-rspec"
33
+ spec.add_development_dependency "fuubar"
34
+ spec.add_development_dependency "webmock"
35
+ end
@@ -0,0 +1,196 @@
1
+ require 'bunny'
2
+ require 'spec_helper'
3
+
4
+ module Sonic
5
+ describe ServiceChecker do
6
+ context "create" do
7
+ context "with valid http arguments" do
8
+ let(:protocol) { :http }
9
+ let(:host) { "localhost" }
10
+ let(:port) { 8081 }
11
+
12
+ subject(:service_checker) { ServiceChecker.new(protocol, host, port) }
13
+ it { should be_kind_of ServiceChecker }
14
+
15
+ describe "attributes" do
16
+ its(:protocol) { should be_kind_of(Symbol) }
17
+ its(:host) { should be_kind_of(String) }
18
+ its(:port) { should be_kind_of(Integer) }
19
+ end
20
+
21
+ context "with responding service" do
22
+ let(:uri) { "#{protocol}://#{host}:#{port}" }
23
+ before { stub_request(:get, uri) }
24
+
25
+ describe '#check_service' do
26
+ subject { service_checker.check_service }
27
+ it { should be_true }
28
+ end
29
+
30
+ context "with a valid response from service" do
31
+ before { service_checker.check_service }
32
+ describe '#response' do
33
+ subject { service_checker.response }
34
+ it { should_not be_nil }
35
+ end
36
+ describe '#error' do
37
+ subject { service_checker.error }
38
+ it { should be_nil }
39
+ end
40
+ end
41
+
42
+ context "with an invalid response from service" do
43
+ before { stub_request(:get, uri).to_return(:status => 500) }
44
+ before { service_checker.check_service }
45
+ describe '#response' do
46
+ subject { service_checker.response }
47
+ it { should_not be_nil }
48
+ end
49
+ describe '#error' do
50
+ subject { service_checker.error }
51
+ it { should == "service error" }
52
+ end
53
+ end
54
+ end
55
+
56
+ context "without a responding service" do
57
+ let(:uri) { "#{protocol}://#{host}:#{port}" }
58
+ before { stub_request(:get, uri).to_raise(SocketError) }
59
+
60
+ describe '#check_service' do
61
+ subject { service_checker.check_service }
62
+ it { should be_false }
63
+ end
64
+
65
+ context "successfuly checked down service" do
66
+ before { service_checker.check_service }
67
+ describe '#response' do
68
+ subject { service_checker.response }
69
+ it { should be_nil }
70
+ end
71
+ describe '#error' do
72
+ subject { service_checker.error }
73
+ it { should == "Exception from WebMock" }
74
+ end
75
+ end
76
+ end
77
+
78
+ end
79
+
80
+ context "with valid tcp arguments" do
81
+ let(:protocol) { :tcp }
82
+ let(:host) { "localhost" }
83
+ let(:port) { 6667 }
84
+ let(:payload) { "test" }
85
+
86
+ subject(:service_checker) { ServiceChecker.new(protocol, host, port, nil, payload) }
87
+ it { should be_kind_of ServiceChecker }
88
+
89
+ describe "attributes" do
90
+ its(:protocol) { should be_kind_of(Symbol) }
91
+ its(:host) { should be_kind_of(String) }
92
+ its(:port) { should be_kind_of(Integer) }
93
+ its(:payload) { should be_kind_of(String) }
94
+ end
95
+
96
+ context "with responding service" do
97
+ describe '#check_service' do
98
+ before(:each) do
99
+ double_socket = double(TCPSocket)
100
+ TCPSocket.stub(:new).with(host, port).and_return(double_socket)
101
+ double_socket.stub(:syswrite).with(payload).and_return(true)
102
+ double_socket.stub(:sysread).with(1).exactly(6).times.and_return("hello\n")
103
+ double_socket.stub(:close)
104
+ end
105
+
106
+ subject { service_checker.check_service }
107
+ it { should be_true }
108
+
109
+ describe '#response' do
110
+ before { service_checker.check_service }
111
+ subject { service_checker.response }
112
+ it { should == "hello\n" }
113
+ end
114
+ end
115
+ end
116
+
117
+ context "without responding service" do
118
+ describe '#check_service' do
119
+ subject { service_checker.check_service }
120
+ it { should be_false }
121
+ end
122
+
123
+ context "successfuly checked down service" do
124
+ before { service_checker.check_service }
125
+ describe '#response' do
126
+ subject { service_checker.response }
127
+ it { should be_nil }
128
+ end
129
+ describe '#error' do
130
+ subject { service_checker.error }
131
+ it { should == "Connection refused - connect(2)" }
132
+ end
133
+ end
134
+ end
135
+
136
+ end
137
+
138
+ context "with valid amqp arguments" do
139
+ let(:protocol) { :amqp }
140
+ let(:host) { "localhost" }
141
+ let(:port) { 567222 }
142
+
143
+ subject(:service_checker) { ServiceChecker.new(protocol, host, port) }
144
+ it { should be_kind_of ServiceChecker }
145
+
146
+ describe "attributes" do
147
+ its(:protocol) { should be_kind_of(Symbol) }
148
+ its(:host) { should be_kind_of(String) }
149
+ its(:port) { should be_kind_of(Integer) }
150
+ end
151
+
152
+ context "with responding service" do
153
+ describe '#check_service' do
154
+ before(:each) do
155
+ double_bunny = double(::Bunny)
156
+ double_bunny.stub(:start)
157
+ double_bunny.stub(:status).and_return(:open)
158
+ double_bunny.stub(:close).and_return(:closed)
159
+ ::Bunny.stub(:new).with({:host => host, :port => port}).and_return(double_bunny)
160
+ end
161
+
162
+ subject { service_checker.check_service }
163
+ it { should be_true }
164
+
165
+ describe '#response' do
166
+ before { service_checker.check_service }
167
+ subject { service_checker.response }
168
+ it { should == :open }
169
+ end
170
+ end
171
+ end
172
+
173
+ context "without responding service" do
174
+ describe '#check_service' do
175
+ subject { service_checker.check_service }
176
+ it { should be_false }
177
+ end
178
+
179
+ context "successfuly checked down service" do
180
+ before { service_checker.check_service }
181
+ describe '#response' do
182
+ subject { service_checker.response }
183
+ it { should be_nil }
184
+ end
185
+ describe '#error' do
186
+ subject { service_checker.error }
187
+ it { should == "Could not establish TCP connection to localhost:567222: " }
188
+ end
189
+ end
190
+ end
191
+
192
+ end
193
+
194
+ end
195
+ end
196
+ end