sonic 0.0.1

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