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.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +1 -0
- data/app/controllers/sonic/results_controller.rb +15 -0
- data/app/views/sonic/results/index.html.erb +22 -0
- data/config/routes.rb +5 -0
- data/fixture_rails_root/.gitignore +15 -0
- data/fixture_rails_root/Gemfile +38 -0
- data/fixture_rails_root/README.rdoc +261 -0
- data/fixture_rails_root/Rakefile +7 -0
- data/fixture_rails_root/app/assets/images/rails.png +0 -0
- data/fixture_rails_root/app/assets/javascripts/application.js +15 -0
- data/fixture_rails_root/app/assets/stylesheets/application.css +13 -0
- data/fixture_rails_root/app/controllers/application_controller.rb +3 -0
- data/fixture_rails_root/app/helpers/application_helper.rb +2 -0
- data/fixture_rails_root/app/mailers/.gitkeep +0 -0
- data/fixture_rails_root/app/models/.gitkeep +0 -0
- data/fixture_rails_root/app/views/layouts/application.html.erb +14 -0
- data/fixture_rails_root/config.ru +4 -0
- data/fixture_rails_root/config/application.rb +62 -0
- data/fixture_rails_root/config/boot.rb +6 -0
- data/fixture_rails_root/config/database.yml +25 -0
- data/fixture_rails_root/config/environment.rb +5 -0
- data/fixture_rails_root/config/environments/development.rb +37 -0
- data/fixture_rails_root/config/environments/production.rb +67 -0
- data/fixture_rails_root/config/environments/test.rb +37 -0
- data/fixture_rails_root/config/initializers/backtrace_silencers.rb +7 -0
- data/fixture_rails_root/config/initializers/inflections.rb +15 -0
- data/fixture_rails_root/config/initializers/mime_types.rb +5 -0
- data/fixture_rails_root/config/initializers/secret_token.rb +7 -0
- data/fixture_rails_root/config/initializers/session_store.rb +8 -0
- data/fixture_rails_root/config/initializers/wrap_parameters.rb +14 -0
- data/fixture_rails_root/config/locales/en.yml +5 -0
- data/fixture_rails_root/config/routes.rb +58 -0
- data/fixture_rails_root/db/seeds.rb +7 -0
- data/fixture_rails_root/lib/assets/.gitkeep +0 -0
- data/fixture_rails_root/lib/tasks/.gitkeep +0 -0
- data/fixture_rails_root/log/.gitkeep +0 -0
- data/fixture_rails_root/public/404.html +26 -0
- data/fixture_rails_root/public/422.html +26 -0
- data/fixture_rails_root/public/500.html +25 -0
- data/fixture_rails_root/public/favicon.ico +0 -0
- data/fixture_rails_root/public/index.html +241 -0
- data/fixture_rails_root/public/robots.txt +5 -0
- data/fixture_rails_root/script/rails +6 -0
- data/fixture_rails_root/test/fixtures/.gitkeep +0 -0
- data/fixture_rails_root/test/functional/.gitkeep +0 -0
- data/fixture_rails_root/test/integration/.gitkeep +0 -0
- data/fixture_rails_root/test/performance/browsing_test.rb +12 -0
- data/fixture_rails_root/test/test_helper.rb +13 -0
- data/fixture_rails_root/test/unit/.gitkeep +0 -0
- data/fixture_rails_root/vendor/assets/javascripts/.gitkeep +0 -0
- data/fixture_rails_root/vendor/assets/stylesheets/.gitkeep +0 -0
- data/fixture_rails_root/vendor/plugins/.gitkeep +0 -0
- data/lib/generators/USAGE +9 -0
- data/lib/generators/sonic_generator.rb +19 -0
- data/lib/generators/templates/README +13 -0
- data/lib/generators/templates/clock.rb +36 -0
- data/lib/generators/templates/config.rb +15 -0
- data/lib/sonic.rb +22 -0
- data/lib/sonic/protocols/amqp.rb +32 -0
- data/lib/sonic/protocols/base.rb +6 -0
- data/lib/sonic/protocols/http.rb +32 -0
- data/lib/sonic/protocols/tcp.rb +58 -0
- data/lib/sonic/service_checker.rb +39 -0
- data/lib/sonic/service_checker_builder.rb +14 -0
- data/lib/sonic/version.rb +3 -0
- data/sonic.gemspec +35 -0
- data/spec/sonic/service_checker_spec.rb +196 -0
- data/spec/spec_helper.rb +28 -0
- metadata +332 -0
data/lib/sonic.rb
ADDED
@@ -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,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
|
data/sonic.gemspec
ADDED
@@ -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
|