cepa-health 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cepa-health.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Jon Williams
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # Cepa Health Check for Rack-based Applications
2
+
3
+ Cepa Health is a Rack Middleware that provides "probes". A probe is a block
4
+ of code executed when the `/healthy` path is accessed. This path will return
5
+ a list of the probe results, most importantly with the HTTP Status Code of
6
+ "200 OK" for an overall successful result, or a "500 Internal Server Error" if
7
+ any probe fails.
8
+
9
+ This path is intended for use by services such as
10
+ [Pingdom](https://www.pingdom.com/) or [New Relic](http://newrelic.com/).
11
+ This path is also for use with Load Balancer that use a health check, such as
12
+ Amazon's [Elastic Load Balancer](http://aws.amazon.com/elasticloadbalancing/).
13
+
14
+ See Discussion before for more details on the use of Cepa Health.
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ gem 'cepa-health'
21
+
22
+ And then execute:
23
+
24
+ $ bundle
25
+
26
+ Equally you can install without a gemfile using:
27
+
28
+ $ gem install cepa-health
29
+
30
+ ## Usage
31
+
32
+ http://rubyonrails.org/
33
+ If you're running a Rails application, you can generate a default initializer with
34
+
35
+ $ rails generate cepa_health:initializer
36
+
37
+ This will create a file `config/initializers/cepa_health.rb`. Refer to that file for more instructions.
38
+
39
+ Alternatively, if you're running a Rack-based application (e.g. using
40
+ [Sinatra Framework](http://www.sinatrarb.com/)) add the following to config.ru. Note this assumes you're running with [Bundler](http://bundler.io/sinatra.html) or loading the cepa-health gem yourself.
41
+
42
+ use CepaHealth::Middleware
43
+
44
+ To define probes, register blocks as:
45
+
46
+ # Create a Probe with the default level of "error"
47
+ CepaHealth.register "Probe Name" do
48
+ record("Other result", true, "This will add another reporting row")
49
+ true # Ultimate result
50
+ end
51
+
52
+ # Create a Probe specifically tagged as level "warn"
53
+ CepaHealth.register "Warning Probe", :warn do
54
+ record("Other result", true, "This will add another reporting row")
55
+ true # Ultimate result
56
+ end
57
+
58
+ The result of these Probes is summarized at the `/healthy` path when you run your Rack application. This will render a HTML table, you can similarly use `/healthy.json` or `healthy.txt` for JSON and Text results respectively. Take a look at the
59
+ [probes directory](https://github.com/cepaorg/cepa-health/tree/master/probes) for
60
+ some examples of probes.
61
+
62
+ By default, `/healthy` will return all probes. You can cut this back using filters. For example, `healthy.txt?filters=warn` will return a Text summary of just the "warn" level Probes. `healthy.txt?filters=error,warn` resturns both "error" and "warn" probes.
63
+
64
+ ## Privacy
65
+
66
+ You may not want your health check available to anyone - either because you want to be private about the results, or you don't want to unnecessarily reveal details of your stack. To provide an extra layer of privacy, you can set a key on your health check. Just add (or comment out in the initalizer):
67
+
68
+ CepaHealth.key = "sekret"
69
+
70
+ The health check will only be available if `key=sekret` is added to the path. If it doesn't match, a blank 404 is returned.
71
+
72
+ This will prevent casual access to your health check.
73
+
74
+ ## Discussion
75
+
76
+ There are already a handful of Rack and Rails-based Health Checks. For example,
77
+ [rack-health](https://github.com/mirakui/rack-health) or
78
+ [rack-ping](https://github.com/jondot/rack-ping).
79
+
80
+ Cepa Health addresses a handful of specific needs:
81
+
82
+ - Different levels of probes. The "error" is appropriate for removing a server from a Load Balancer and raising alarms. The "warn" level may be less serious, such as a failed Delayed Job. You way still wish to raise an alert, but perhaps of different severity.
83
+ - Simiarly, there may be other needs for uptime reporting. In this case you may only wish to measure probes that directly affect site usage.
84
+ - Cepa Health can be stubbed out by simply adding a blank `healthy.txt` to the root directory of the given path.
85
+
86
+
87
+ ## Contributing
88
+
89
+ 1. Fork it
90
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
91
+ 3. Make sure you have some tests or way of validating the feature.
92
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
93
+ 5. Push to the branch (`git push origin my-new-feature`)
94
+ 6. Create new Pull Request
95
+ 7. ... and thanks!
96
+
97
+ ## License
98
+
99
+ MIT Licensed. See LICENSE.txt
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake'
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc 'Runs the RSpecs'
6
+ RSpec::Core::RakeTask.new('spec') do |t|
7
+ t.rspec_opts = ['--color']
8
+ end
9
+
10
+ task :default => :spec
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cepa-health/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "cepa-health"
8
+ gem.version = CepaHealth::VERSION
9
+ gem.authors = ["Jon Williams"]
10
+ gem.email = ["jon@jonathannen.com"]
11
+ gem.description = %q{Health Check Middleware for Rails and Rack-based Applications}
12
+ gem.summary = %q{Provides the facility for probes that are evaluated when a health URL is accessed.}
13
+ gem.license = "MIT"
14
+ gem.homepage = "https://github.com/cepaorg/cepa-health"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_dependency 'rack', '>= 1.2.0'
22
+ gem.add_development_dependency 'rspec', '>= 2.0.0'
23
+ end
@@ -0,0 +1,35 @@
1
+ require 'rails/generators'
2
+ require 'securerandom'
3
+
4
+ module CepaHealth
5
+
6
+ class InitializerGenerator < Rails::Generators::Base
7
+
8
+ def create_initializer_file
9
+ key = SecureRandom.hex(3)
10
+ create_file "config/initializers/cepa_health.rb", <<-CONTENT
11
+ # Configure Cepa Health checks.
12
+ # See: https://github.com/cepaorg/cepa-health
13
+
14
+ # # Comment out the following to remove the standard probes.
15
+ CepaHealth.load_probes
16
+
17
+ # # Comment out the below if you'd like your health check protected
18
+ # # by a key. The new health link will now be whatever the key is. In this
19
+ # # example, "/healthcheck?key=#{key}"
20
+ # CepaHealth.key = "#{key}"
21
+
22
+ # # Add the following to bring a standard probe back after removing the above.
23
+ # CepaHealth.load_probe(:rails)
24
+
25
+ # # And/Or you can also add your own probes
26
+ # CepaHealth.register "Probe Name" do
27
+ # record("Other result", true, "This will add another reporting row")
28
+ # true # Ultimate result
29
+ # end
30
+ CONTENT
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,125 @@
1
+ # encoding: utf-8
2
+ # Copyright © 2013 Jon Williams. See LICENSE.txt for details.
3
+ require 'cgi'
4
+
5
+ module CepaHealth
6
+
7
+ class Middleware
8
+ DEFAULT_PATH = /\A\/healthy(\.html|\.json|\.txt)?\z/
9
+
10
+ def initialize(app, options={})
11
+ @app = app
12
+ @path = options.fetch(:path, DEFAULT_PATH)
13
+ end
14
+
15
+ def call(env)
16
+ path = env['PATH_INFO']
17
+ path_matches?(path) ? process(path, env) : @app.call(env)
18
+ end
19
+
20
+ protected
21
+
22
+ def determine_mime_type(path)
23
+ case path.split('.').last.downcase
24
+ when 'json' then 'application/json'
25
+ when 'txt' then 'text/plain'
26
+ else 'text/html'
27
+ end
28
+ end
29
+
30
+ def process(path, env)
31
+ query = env['QUERY_STRING']
32
+ params = Rack::Utils.parse_nested_query(query)
33
+ filters = params['filters']
34
+ filters = filters.nil? ? [] : filters.split(",").map { |v| v.strip }
35
+ result = CepaHealth.execute(*filters)
36
+
37
+ unless CepaHealth.key.nil?
38
+ key = params['key']
39
+ return [404, {}, [""]] unless key == CepaHealth.key
40
+ end
41
+
42
+ mime = determine_mime_type(path)
43
+
44
+ body = case mime
45
+ when 'text/plain' then render_text(result)
46
+ else render_html(result)
47
+ end
48
+
49
+ [result.success? ? 200: 500, { 'Content-Type' => "#{mime}; charset=utf-8" }, [body]]
50
+ end
51
+
52
+ def path_matches?(path)
53
+ case @path
54
+ when Proc then @path.call(path)
55
+ when Regexp then path =~ @path
56
+ else @path.to_s == path
57
+ end
58
+ end
59
+
60
+ def render_html(result)
61
+ rows = result.records.map do |name, status, comment|
62
+ stat = status ? "<td class='status ok'>OK</td>" : "<td class='status fail'>FAIL</td>"
63
+ "<tr>#{stat}<td class='name'>#{CGI::escapeHTML(name)}</td><td>#{CGI::escapeHTML(comment)}</td></tr>"
64
+ end
65
+ <<-HTML
66
+ <!DOCTYPE html>
67
+ <html>
68
+ <head>
69
+ <title>Health Check</title>
70
+ <style type='text/css'>
71
+ body {
72
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
73
+ }
74
+ div.container {
75
+ margin: 0 auto;
76
+ width: 960px;
77
+ }
78
+
79
+ h1 { font-size: 20px; font-weight: bold; }
80
+ h2 { font-size: 22px; font-weight: bold; }
81
+ h2 span { font-size: 36px; }
82
+
83
+ table {
84
+ border-bottom: 1px solid #999;
85
+ border-top: 1px solid #999;
86
+ margin-top: 10px;
87
+ width: 100%;
88
+ }
89
+
90
+ td {
91
+ font-size: 14px;
92
+ padding: 5px;
93
+ }
94
+ td.name { background: #f7f7f7; font-weight: bold; width: 200px; }
95
+ td.status { font-weight: bold; text-align: center; width: 50px; }
96
+ td.status.fail { background: #fdd; color: #c00; }
97
+ td.status.ok { background: #dfd; color: #0c0; }
98
+
99
+ .fail h2 { color: #600; }
100
+ .fail span { color: #c00; text-shadow: 2px 2px 0 #600; }
101
+ .ok h2 { color: #060; }
102
+ .ok span { color: #0c0; text-shadow: 2px 2px 0 #060; }
103
+
104
+ </style>
105
+ </head>
106
+ <body class='#{ result.success? ? 'ok' : 'fail'}'>
107
+ <div class='container'>
108
+ <h1>Health Check</h1>
109
+ <h2>#{ result.success? ? "<span>✔</span> Great, the Application is Healthy" : "<span>✘</span> Damn, something is broken"}</h2>
110
+ <table>#{ rows * "\n" }</table>
111
+ </div>
112
+ </body>
113
+ </html>
114
+ HTML
115
+ end
116
+
117
+ def render_text(result)
118
+ body = "#Entry\t#Status\t#Comment\n"
119
+ body << (result.success? ? "Overall\tSuccess\n" : "Overall\tFailure\n")
120
+ body + result.records.map { |a,b,c| "#{a}\t#{b ? "Success" : "Failure"}\t#{c}" } * "\n"
121
+ end
122
+
123
+ end
124
+
125
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+ # Copyright © 2013 Jon Williams. See LICENSE.txt for details.
3
+
4
+ module CepaHealth
5
+
6
+ class Railtie < Rails::Railtie
7
+
8
+ initializer "cepa_health.configure_rails_initialization" do |app|
9
+ app.middleware.use CepaHealth::Middleware
10
+ end
11
+
12
+ end
13
+
14
+ end
@@ -0,0 +1,4 @@
1
+ module CepaHealth
2
+ VERSION = "0.1.0"
3
+ end
4
+
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+ # Copyright © 2013 Jon Williams. See LICENSE.txt for details.
3
+
4
+ module CepaHealth
5
+ attr_accessor :success
6
+
7
+ # Container that holds the result of the probe execution
8
+ class Result
9
+ attr_reader :records
10
+
11
+ def execute(name, block)
12
+ @success &&= !!(v = instance_exec(&block))
13
+ record(name, v, v ? "Success" : "Failed")
14
+ end
15
+
16
+ def initialize
17
+ @records = []
18
+ @success = true
19
+ end
20
+
21
+ def record(name, status, comment)
22
+ @records << [name, status, comment]
23
+ end
24
+
25
+ def success?
26
+ @success
27
+ end
28
+
29
+ end
30
+
31
+ class << self
32
+ attr_accessor :key
33
+ attr_reader :probes
34
+
35
+ def clear_probes!
36
+ @probes = {}
37
+ end
38
+
39
+ # Executes the probes.
40
+ # @param [ Array<String> ] filters to the given levels when the probes
41
+ # were registered. If no filters are specified, all probes are resulted.
42
+ def execute(*filters)
43
+ result = CepaHealth::Result.new
44
+ filters = filters.map { |v| v.to_s }
45
+ selected = filters.empty? ? probes : probes.select { |k,v| filters.include?(k) }
46
+ selected.values.flatten(1).each { |v| result.execute(*v) }
47
+ result
48
+ end
49
+
50
+ # Loads an individual probe
51
+ def load_probe(name)
52
+ dir = File.expand_path(File.join(File.dirname(File.expand_path(__FILE__)), '..', 'probes'))
53
+ Dir[File.join(dir, name)].each { |v| require(v) }
54
+ end
55
+
56
+ # Scans the probes directory for Ruby files.
57
+ def load_probes
58
+ dir = File.expand_path(File.join(File.dirname(File.expand_path(__FILE__)), '..', 'probes'))
59
+ Dir[File.join(dir, "**/*.rb")].each { |v| require(v) }
60
+ end
61
+
62
+ # Registers the given block as a probe. An optional level can be supplied,
63
+ # which can be used as a filter.
64
+ def register(name, level = :error, &block)
65
+ list = probes[level.to_s] ||= []
66
+ list << [name, block]
67
+ self
68
+ end
69
+
70
+ end
71
+ @probes = {}
72
+ @key = nil
73
+
74
+ end
75
+
76
+ require "cepa-health/middleware"
77
+ require "cepa-health/version"
78
+
79
+ # Railtie to add the Middleware Automatically
80
+ if defined?(Rails)
81
+ require 'cepa-health/generator'
82
+ require 'cepa-health/railtie'
83
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+ # Copyright © 2013 Jon Williams. See LICENSE.txt for details.
3
+
4
+ if defined?(Delayed)
5
+
6
+ CepaHealth.register "Delayed Job" do
7
+ record "Rails Major Version", true, Rails.version.split('.').first
8
+ true
9
+ end
10
+
11
+ end
data/probes/mongoid.rb ADDED
@@ -0,0 +1,3 @@
1
+ # encoding: utf-8
2
+ # Copyright © 2013 Jon Williams. See LICENSE.txt for details.
3
+
data/probes/rails.rb ADDED
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+ # Copyright © 2013 Jon Williams. See LICENSE.txt for details.
3
+
4
+ if defined?(Rails)
5
+
6
+ # A Trivial Rails probe.
7
+ CepaHealth.register "Rails" do
8
+ record "Rails Major Version", true, Rails.version.split('.').first
9
+ true
10
+ end
11
+
12
+ end
data/probes/sqlite.rb ADDED
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+ # Copyright © 2013 Jon Williams. See LICENSE.txt for details.
3
+
4
+ if defined?(ActiveRecord) && defined?(SQLite3)
5
+
6
+ CepaHealth.register "SQLite" do
7
+ begin
8
+ ActiveRecord::Base.connection.exec_query("PRAGMA quick_check")
9
+ true
10
+ rescue Exception => e
11
+ record("SQLite Failure", false, e.inspect)
12
+ false
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,195 @@
1
+ # encoding: utf-8
2
+ # Copyright © 2013 Jon Williams. See LICENSE.txt for details.
3
+
4
+ require 'rack/lint'
5
+ require 'rack/mock'
6
+ require 'cepa-health'
7
+
8
+ describe CepaHealth::Middleware do
9
+
10
+ before(:each) do
11
+ CepaHealth.clear_probes!
12
+ CepaHealth.key = nil
13
+ end
14
+
15
+ let(:rackapp) do
16
+ app = ->(e) { [200, { 'Content-Type' => 'text/plain' }, ["Boom"]] }
17
+ Rack::Lint.new CepaHealth::Middleware.new(app)
18
+ end
19
+
20
+ it "should not match other URLs" do
21
+ code, headers, body = get("/someotherpage.html")
22
+ body.should == "Boom"
23
+ end
24
+
25
+ it "should match the healthy URL" do
26
+ code, headers, body = get("/healthy.html")
27
+ code.should == 200
28
+ body.should_not == "Boom"
29
+ end
30
+
31
+ it "should return a 200 OK for passing probes" do
32
+ CepaHealth.register("VeryUniqueTestIFear") { true }
33
+ code, headers, body = get("/healthy.html")
34
+ code.should == 200
35
+ body.should =~ /VeryUniqueTestIFear/
36
+ end
37
+
38
+ it "should return a 500 Error for failing probes" do
39
+ CepaHealth.register("VeryUniqueTestIFear") { true }
40
+ CepaHealth.register("TotallyUniqueFailure") { false }
41
+ code, headers, body = get("/healthy.html")
42
+ code.should == 500
43
+ body.should =~ /TotallyUniqueFailure/
44
+ end
45
+
46
+ it "should return a 404 if a key is set and not specified" do
47
+ CepaHealth.register("VeryUniqueTestIFear") { true }
48
+ CepaHealth.key = 'stone'
49
+ code, headers, body = get("/healthy.html")
50
+ code.should == 404
51
+ body.should == ""
52
+ end
53
+
54
+ it "should return successful if a key is set and specified" do
55
+ CepaHealth.register("VeryUniqueTestIFear") { true }
56
+ CepaHealth.key = 'stone'
57
+ code, headers, body = get("/healthy.html", 'QUERY_STRING' => 'key=stone')
58
+ code.should == 200
59
+ body.should =~ /VeryUniqueTestIFear/
60
+ end
61
+
62
+ it "should filter responses if they have different levels" do
63
+ CepaHealth.register("error1", "error") { false }
64
+ CepaHealth.register("error2", "error") { false }
65
+ CepaHealth.register("warn1", "warn") { true }
66
+ CepaHealth.register("warn2", "warn") { true }
67
+ CepaHealth.key = 'stone'
68
+
69
+ code, headers, body = get("/healthy.txt", 'QUERY_STRING' => 'key=stone')
70
+ code.should == 500
71
+ body.should =~ /error1/
72
+ body.should =~ /warn1/
73
+
74
+ code, headers, body = get("/healthy.txt", 'QUERY_STRING' => 'key=stone&filters=error,warn')
75
+ code.should == 500
76
+ body.should =~ /error1/
77
+ body.should =~ /warn1/
78
+
79
+ code, headers, body = get("/healthy.txt", 'QUERY_STRING' => 'key=stone&filters=error,other2')
80
+ code.should == 500
81
+ body.should =~ /error1/
82
+ body.should_not =~ /warn1/
83
+
84
+ code, headers, body = get("/healthy.txt", 'QUERY_STRING' => 'key=stone&filters=warn')
85
+ code.should == 200
86
+ body.should_not =~ /error1/
87
+ body.should =~ /warn1/
88
+ end
89
+
90
+ protected
91
+
92
+ def get(url, opts = {})
93
+ code, headers, lint = rackapp.call(Rack::MockRequest.env_for(url, opts))
94
+ body = ""; lint.each { |v| body << v.to_s }
95
+ [code, headers, body]
96
+ end
97
+
98
+ end
99
+ # describe Rack::Health do
100
+ # def env(url='/', *args)
101
+ # Rack::MockRequest.env_for(url, *args)
102
+ # end
103
+
104
+ # let(:base_app) do
105
+ # lambda do |env|
106
+ # [200, {'Content-Type' => 'text/plain'}, ["I'm base_app"]]
107
+ # end
108
+ # end
109
+ # let(:app) { Rack::Lint.new Rack::Health.new(base_app, rack_health_options) }
110
+ # let(:rack_health_options) { {} }
111
+ # let(:status) { subject[0] }
112
+ # let(:body) { str = ''; subject[2].each {|s| str += s }; str }
113
+
114
+ # describe 'with default options' do
115
+ # let(:rack_health_options) { {} }
116
+
117
+ # describe '/' do
118
+ # subject { app.call env('/') }
119
+
120
+ # it { status.should == 200 }
121
+ # it { body.should == "I'm base_app" }
122
+ # end
123
+
124
+ # describe '/rack_health' do
125
+ # subject { app.call env('/rack_health') }
126
+
127
+ # it { status.should == 200 }
128
+ # it { body.should == 'Rack::Health says "healthy"' }
129
+ # end
130
+ # end
131
+
132
+ # describe 'with :sick_if' do
133
+ # subject { app.call env('/rack_health') }
134
+
135
+ # describe '== lambda { true }' do
136
+ # let(:rack_health_options) { { :sick_if => lambda { true } } }
137
+
138
+ # it { status.should == 503 }
139
+ # it { body.should == 'Rack::Health says "sick"' }
140
+ # end
141
+
142
+ # describe '== lambda { false }' do
143
+ # let(:rack_health_options) { { :sick_if => lambda { false } } }
144
+
145
+ # it { status.should == 200 }
146
+ # it { body.should == 'Rack::Health says "healthy"' }
147
+ # end
148
+ # end
149
+
150
+ # describe 'with :status' do
151
+ # let(:status_proc) { lambda {|healthy| healthy ? 202 : 404 } }
152
+ # subject { app.call env('/rack_health') }
153
+
154
+ # context 'healthy' do
155
+ # let(:rack_health_options) { { :sick_if => lambda { false }, :status => status_proc } }
156
+
157
+ # it { status.should == 202 }
158
+ # it { body.should == 'Rack::Health says "healthy"' }
159
+ # end
160
+
161
+ # context 'sick' do
162
+ # let(:rack_health_options) { { :sick_if => lambda { true }, :status => status_proc } }
163
+
164
+ # it { status.should == 404 }
165
+ # it { body.should == 'Rack::Health says "sick"' }
166
+ # end
167
+ # end
168
+
169
+ # describe 'with :body' do
170
+ # let(:body_proc) { lambda {|healthy| healthy ? 'fine' : 'bad' } }
171
+ # subject { app.call env('/rack_health') }
172
+
173
+ # context 'healthy' do
174
+ # let(:rack_health_options) { { :sick_if => lambda { false }, :body => body_proc } }
175
+
176
+ # it { status.should == 200 }
177
+ # it { body.should == 'fine' }
178
+ # end
179
+
180
+ # context 'sick' do
181
+ # let(:rack_health_options) { { :sick_if => lambda { true }, :body => body_proc } }
182
+
183
+ # it { status.should == 503 }
184
+ # it { body.should == 'bad' }
185
+ # end
186
+ # end
187
+
188
+ # describe 'with :path' do
189
+ # subject { app.call env('/how_are_you') }
190
+ # let(:rack_health_options) { { :path => '/how_are_you' } }
191
+
192
+ # it { status.should == 200 }
193
+ # it { body.should == 'Rack::Health says "healthy"' }
194
+ # end
195
+ # end
@@ -0,0 +1,146 @@
1
+ # encoding: utf-8
2
+ # Copyright © 2013 Jon Williams. See LICENSE.txt for details.
3
+
4
+ require 'cepa-health'
5
+
6
+ describe CepaHealth do
7
+
8
+ before(:each) { CepaHealth.clear_probes! }
9
+
10
+ it "should register and execute successful probes" do
11
+ standard_setup
12
+ should_be(true, 4)
13
+ end
14
+
15
+ it "should register a failure on any unsuccessful probe" do
16
+ standard_setup
17
+ CepaHealth.register("Fail") { false }
18
+ CepaHealth.register("Four") { true }
19
+ should_be(false, 6)
20
+ end
21
+
22
+ it "should allow the registration of probes in levels" do
23
+ CepaHealth.register("One") { false }
24
+ CepaHealth.register("Two", "error") { false }
25
+ CepaHealth.register("Three", "warn") { true }
26
+ CepaHealth.register("Four", "warn") { true }
27
+ CepaHealth.register("Five", "warn") { true }
28
+ should_be(false, 5)
29
+ should_be(false, 2, "error")
30
+ should_be(true, 3, "warn")
31
+ should_be(false, 5, %w{error warn other})
32
+ end
33
+
34
+ protected
35
+
36
+ def should_be(ok, record_length, filters = [])
37
+ r = CepaHealth.execute(*filters)
38
+ r.success?.should == ok
39
+ r.records.length.should == record_length
40
+ r.success?
41
+ end
42
+
43
+ def standard_setup
44
+ CepaHealth.register("One") { true }
45
+ CepaHealth.register("Two") { true }
46
+ CepaHealth.register("Three") { record("Three-B", true, "") }
47
+ end
48
+
49
+ end
50
+ # describe Rack::Health do
51
+ # def env(url='/', *args)
52
+ # Rack::MockRequest.env_for(url, *args)
53
+ # end
54
+
55
+ # let(:base_app) do
56
+ # lambda do |env|
57
+ # [200, {'Content-Type' => 'text/plain'}, ["I'm base_app"]]
58
+ # end
59
+ # end
60
+ # let(:app) { Rack::Lint.new Rack::Health.new(base_app, rack_health_options) }
61
+ # let(:rack_health_options) { {} }
62
+ # let(:status) { subject[0] }
63
+ # let(:body) { str = ''; subject[2].each {|s| str += s }; str }
64
+
65
+ # describe 'with default options' do
66
+ # let(:rack_health_options) { {} }
67
+
68
+ # describe '/' do
69
+ # subject { app.call env('/') }
70
+
71
+ # it { status.should == 200 }
72
+ # it { body.should == "I'm base_app" }
73
+ # end
74
+
75
+ # describe '/rack_health' do
76
+ # subject { app.call env('/rack_health') }
77
+
78
+ # it { status.should == 200 }
79
+ # it { body.should == 'Rack::Health says "healthy"' }
80
+ # end
81
+ # end
82
+
83
+ # describe 'with :sick_if' do
84
+ # subject { app.call env('/rack_health') }
85
+
86
+ # describe '== lambda { true }' do
87
+ # let(:rack_health_options) { { :sick_if => lambda { true } } }
88
+
89
+ # it { status.should == 503 }
90
+ # it { body.should == 'Rack::Health says "sick"' }
91
+ # end
92
+
93
+ # describe '== lambda { false }' do
94
+ # let(:rack_health_options) { { :sick_if => lambda { false } } }
95
+
96
+ # it { status.should == 200 }
97
+ # it { body.should == 'Rack::Health says "healthy"' }
98
+ # end
99
+ # end
100
+
101
+ # describe 'with :status' do
102
+ # let(:status_proc) { lambda {|healthy| healthy ? 202 : 404 } }
103
+ # subject { app.call env('/rack_health') }
104
+
105
+ # context 'healthy' do
106
+ # let(:rack_health_options) { { :sick_if => lambda { false }, :status => status_proc } }
107
+
108
+ # it { status.should == 202 }
109
+ # it { body.should == 'Rack::Health says "healthy"' }
110
+ # end
111
+
112
+ # context 'sick' do
113
+ # let(:rack_health_options) { { :sick_if => lambda { true }, :status => status_proc } }
114
+
115
+ # it { status.should == 404 }
116
+ # it { body.should == 'Rack::Health says "sick"' }
117
+ # end
118
+ # end
119
+
120
+ # describe 'with :body' do
121
+ # let(:body_proc) { lambda {|healthy| healthy ? 'fine' : 'bad' } }
122
+ # subject { app.call env('/rack_health') }
123
+
124
+ # context 'healthy' do
125
+ # let(:rack_health_options) { { :sick_if => lambda { false }, :body => body_proc } }
126
+
127
+ # it { status.should == 200 }
128
+ # it { body.should == 'fine' }
129
+ # end
130
+
131
+ # context 'sick' do
132
+ # let(:rack_health_options) { { :sick_if => lambda { true }, :body => body_proc } }
133
+
134
+ # it { status.should == 503 }
135
+ # it { body.should == 'bad' }
136
+ # end
137
+ # end
138
+
139
+ # describe 'with :path' do
140
+ # subject { app.call env('/how_are_you') }
141
+ # let(:rack_health_options) { { :path => '/how_are_you' } }
142
+
143
+ # it { status.should == 200 }
144
+ # it { body.should == 'Rack::Health says "healthy"' }
145
+ # end
146
+ # end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cepa-health
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jon Williams
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.2.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.2.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 2.0.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 2.0.0
46
+ description: Health Check Middleware for Rails and Rack-based Applications
47
+ email:
48
+ - jon@jonathannen.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - Gemfile
55
+ - LICENSE.txt
56
+ - README.md
57
+ - Rakefile
58
+ - cepa-health.gemspec
59
+ - lib/cepa-health.rb
60
+ - lib/cepa-health/generator.rb
61
+ - lib/cepa-health/middleware.rb
62
+ - lib/cepa-health/railtie.rb
63
+ - lib/cepa-health/version.rb
64
+ - probes/delayed_job.rb
65
+ - probes/mongoid.rb
66
+ - probes/rails.rb
67
+ - probes/sqlite.rb
68
+ - spec/cepa_health_middleware_spec.rb
69
+ - spec/cepa_health_spec.rb
70
+ homepage: https://github.com/cepaorg/cepa-health
71
+ licenses:
72
+ - MIT
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 1.8.25
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: Provides the facility for probes that are evaluated when a health URL is
95
+ accessed.
96
+ test_files:
97
+ - spec/cepa_health_middleware_spec.rb
98
+ - spec/cepa_health_spec.rb