goodguide-pinglish 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/Gemfile +2 -0
- data/LICENSE +20 -0
- data/README.md +119 -0
- data/goodguide-pinglish.gemspec +19 -0
- data/lib/pinglish/check.rb +21 -0
- data/lib/pinglish.rb +146 -0
- data/script/bootstrap +9 -0
- data/script/release +38 -0
- data/script/test +9 -0
- data/test/check_test.rb +23 -0
- data/test/helper.rb +2 -0
- data/test/pinglish_test.rb +262 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 21349b68ac1102b95ac9e8e37ce9a8199af64d56
|
4
|
+
data.tar.gz: 65b356ff81337fa6e66d58547cec1eb8b1ff3fa5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c6923231c568bcc36da5dc6569b57d23118fd64e14238a05b365409c2f82b189e3df45d8ba729b810e92f866cef66d59b83600b5e489b05b2afddfdad26cce05
|
7
|
+
data.tar.gz: 88ec461eeac036dcfde251f0b62fae7f3ad6a9cb5c17598528b9e20baae72b5d22743815208df81f323b40e7aab81b82f20b79956916f45be7bdd7143cdb0166
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 John Barnette, Will Farrington, and contributors.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
# Pinglish
|
2
|
+
|
3
|
+
A simple Rack middleware for checking application health. Pinglish
|
4
|
+
exposes a `/_ping` resource via HTTP `GET`, returning JSON that
|
5
|
+
conforms to the spec below.
|
6
|
+
|
7
|
+
## The Spec
|
8
|
+
|
9
|
+
0. The application __must__ respond to `GET /_ping` as an HTTP request.
|
10
|
+
|
11
|
+
0. The request handler __should__ check the health of all services the
|
12
|
+
application depends on, answering questions like, "Can I query
|
13
|
+
agains my MySQL database," "Can I create/read keys in Redis," or "How
|
14
|
+
many docs are in my ElasticSearch index?"
|
15
|
+
|
16
|
+
0. The response __must__ return within 29 seconds. This is one second
|
17
|
+
less than the default timeout for many monitoring services.
|
18
|
+
|
19
|
+
0. The response __must__ return an `HTTP 200 OK` status code if all
|
20
|
+
health checks pass.
|
21
|
+
|
22
|
+
0. The response __must__ return an `HTTP 503 SERVICE UNAVAILABLE`
|
23
|
+
status code if any health checks fail.
|
24
|
+
|
25
|
+
0. The response __must__ be of Content-Type `application/json;
|
26
|
+
charset=UTF-8`.
|
27
|
+
|
28
|
+
0. The response __must__ be valid JSON no matter what, even if JSON
|
29
|
+
serialization or other fundamental code fails.
|
30
|
+
|
31
|
+
0. The response __must__ contain a `"status"` key set either to `"ok"`
|
32
|
+
or `"failures"`.
|
33
|
+
|
34
|
+
0. The response __must__ contain a `"now"` key set to the current
|
35
|
+
server's time in seconds since epoch as a string.
|
36
|
+
|
37
|
+
0. If the `"status"` key is set to `"failures"`, the response __may__
|
38
|
+
contain a `"failures"` key set to an Array of string names
|
39
|
+
representing failed checks.
|
40
|
+
|
41
|
+
0. If the `"status"` key is set to `"failures"`, the response __may__
|
42
|
+
contain a `"timeouts"` key set to an Array of string names
|
43
|
+
representing checks that exceeded an implementation-specific
|
44
|
+
individual timeout.
|
45
|
+
|
46
|
+
0. The response body __may__ contain any other top-level keys to
|
47
|
+
supply additional data about services the application consumes, but
|
48
|
+
all values must be strings, arrays of strings, or hashes where both
|
49
|
+
keys and values are strings.
|
50
|
+
|
51
|
+
### An Example Response
|
52
|
+
|
53
|
+
```javascript
|
54
|
+
{
|
55
|
+
|
56
|
+
// These two keys will always exist.
|
57
|
+
|
58
|
+
"now": "1359055102",
|
59
|
+
"status": "failures",
|
60
|
+
|
61
|
+
// This key may only exist when a named check has failed.
|
62
|
+
|
63
|
+
"failures": ["db"],
|
64
|
+
|
65
|
+
// This key may only exist when a named check exceeds its timeout.
|
66
|
+
|
67
|
+
"timeouts": ["really-long-check"],
|
68
|
+
|
69
|
+
// Keys like this may exist to provide extra information about
|
70
|
+
// healthy services, like the number of objects in an S3 bucket.
|
71
|
+
|
72
|
+
"s3": "127"
|
73
|
+
}
|
74
|
+
```
|
75
|
+
|
76
|
+
## The Middleware
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
require "pinglish"
|
80
|
+
|
81
|
+
use Pinglish do |ping|
|
82
|
+
|
83
|
+
# A single unnamed check is the simplest possible way to use
|
84
|
+
# Pinglish, and you'll probably never want combine it with other
|
85
|
+
# named checks. An unnamed check contributes to overall success or
|
86
|
+
# failure, but never adds additional data to the response.
|
87
|
+
|
88
|
+
ping.check do
|
89
|
+
App.healthy?
|
90
|
+
end
|
91
|
+
|
92
|
+
# A named check like this can provide useful summary information
|
93
|
+
# when it succeeds. In this case, a top-level "db" key will appear
|
94
|
+
# in the response containing the number of items in the database. If
|
95
|
+
# a check returns nil, no key will be added to the response.
|
96
|
+
|
97
|
+
ping.check :db do
|
98
|
+
App.db.items.size
|
99
|
+
end
|
100
|
+
|
101
|
+
# By default, checks time out after one second. You can override
|
102
|
+
# this with the :timeout option, but be aware that no combination of
|
103
|
+
# checks is ever allowed to exceed the overall 29 second limit.
|
104
|
+
|
105
|
+
ping.check :long, :timeout => 5 do
|
106
|
+
App.dawdle
|
107
|
+
end
|
108
|
+
|
109
|
+
# Signal check failure by raising an exception. Any exception will do.
|
110
|
+
ping.check :fails do
|
111
|
+
raise "Everything's ruined."
|
112
|
+
end
|
113
|
+
|
114
|
+
# Additionally, any check that returns false is counted as a failure.
|
115
|
+
ping.check :false_fails do
|
116
|
+
false
|
117
|
+
end
|
118
|
+
end
|
119
|
+
```
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = "goodguide-pinglish"
|
5
|
+
gem.version = "1.0.0"
|
6
|
+
gem.authors = ["John Barnette", "Will Farrington"]
|
7
|
+
gem.email = ["jbarnette@github.com", "wfarr@github.com"]
|
8
|
+
gem.description = "A simple Rack middleware for checking app health."
|
9
|
+
gem.summary = "/_ping your way to freedom."
|
10
|
+
gem.homepage = "https://github.com/goodguide/pinglish"
|
11
|
+
|
12
|
+
gem.files = `git ls-files`.split($/)
|
13
|
+
gem.test_files = gem.files.grep(/^test/)
|
14
|
+
gem.require_paths = ["lib"]
|
15
|
+
|
16
|
+
gem.add_dependency "rack"
|
17
|
+
gem.add_development_dependency "minitest", "~> 4.5"
|
18
|
+
gem.add_development_dependency "rack-test", "~> 0.6"
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Pinglish
|
2
|
+
class Check
|
3
|
+
attr_reader :group
|
4
|
+
attr_reader :name
|
5
|
+
attr_reader :timeout
|
6
|
+
|
7
|
+
def initialize(name, options = nil, &block)
|
8
|
+
options ||= {}
|
9
|
+
@group = options[:group]
|
10
|
+
@name = name
|
11
|
+
@timeout = options[:timeout] || 1
|
12
|
+
@block = block
|
13
|
+
end
|
14
|
+
|
15
|
+
# Call this check's behavior, returning the result of the block.
|
16
|
+
|
17
|
+
def call(*args, &block)
|
18
|
+
@block.call *args, &block
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/pinglish.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
require "json"
|
2
|
+
require "pinglish/check"
|
3
|
+
require "rack/request"
|
4
|
+
require "timeout"
|
5
|
+
|
6
|
+
# This Rack app provides an endpoint for configurable
|
7
|
+
# system health checks. It's intended to be consumed by machines.
|
8
|
+
|
9
|
+
class Pinglish
|
10
|
+
|
11
|
+
# The HTTP headers sent for every response.
|
12
|
+
HEADERS = {
|
13
|
+
"Content-Type" => "application/json; charset=UTF-8"
|
14
|
+
}
|
15
|
+
|
16
|
+
# Raised when a check exceeds its timeout.
|
17
|
+
class TooLong < RuntimeError; end
|
18
|
+
|
19
|
+
# Create a new instance of the app, with optional parameter `:max` timeout in seconds (default: `29`); yields itself to an optional block for configuring checks.
|
20
|
+
def initialize(options=nil, &block)
|
21
|
+
options ||= {}
|
22
|
+
|
23
|
+
@checks = {}
|
24
|
+
@max = options[:max] || 29 # seconds
|
25
|
+
|
26
|
+
yield self if block_given?
|
27
|
+
end
|
28
|
+
|
29
|
+
def call(env)
|
30
|
+
request = Rack::Request.new(env)
|
31
|
+
|
32
|
+
begin
|
33
|
+
timeout @max do
|
34
|
+
results = {}
|
35
|
+
|
36
|
+
selected_checks(request.params).each do |check|
|
37
|
+
begin
|
38
|
+
timeout(check.timeout) do
|
39
|
+
results[check.name] = check.call
|
40
|
+
end
|
41
|
+
rescue => e
|
42
|
+
results[check.name] = e
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
failed = results.values.any? { |v| failure? v }
|
47
|
+
http_status = failed ? 503 : 200
|
48
|
+
text_status = failed ? "failures" : "ok"
|
49
|
+
|
50
|
+
data = {
|
51
|
+
:now => Time.now.to_i.to_s,
|
52
|
+
:status => text_status
|
53
|
+
}
|
54
|
+
|
55
|
+
results.each do |name, value|
|
56
|
+
if failure?(value)
|
57
|
+
# If a check fails its name is added to a `failures` array.
|
58
|
+
# If the check failed because it timed out, its name is
|
59
|
+
# added to a `timeouts` array instead.
|
60
|
+
|
61
|
+
key = timeout?(value) ? :timeouts : :failures
|
62
|
+
(data[key] ||= []) << name
|
63
|
+
|
64
|
+
if key == :failures and value.is_a?(Exception)
|
65
|
+
data[name] = {
|
66
|
+
state: :error,
|
67
|
+
exception: value.class.name,
|
68
|
+
message: value.message,
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
elsif value
|
73
|
+
# If the check passed and returned a value, the stringified
|
74
|
+
# version of the value is returned under the `name` key.
|
75
|
+
|
76
|
+
data[name] = value
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
[http_status, HEADERS, [JSON.generate(data)]]
|
81
|
+
end
|
82
|
+
|
83
|
+
rescue Exception => ex
|
84
|
+
# Something catastrophic happened. We can't even run the checks
|
85
|
+
# and render a JSON response. Fall back on a pre-rendered string
|
86
|
+
# and interpolate the current epoch time.
|
87
|
+
|
88
|
+
now = Time.now.to_i.to_s
|
89
|
+
|
90
|
+
body = <<-EOF.gsub(/^ {6}/, '')
|
91
|
+
{
|
92
|
+
"status": "failures",
|
93
|
+
"now": "#{now}",
|
94
|
+
"error": {
|
95
|
+
"class": "#{ex.class.name}",
|
96
|
+
"message": "#{ex.message.tr('"', '')}"
|
97
|
+
}
|
98
|
+
}
|
99
|
+
EOF
|
100
|
+
|
101
|
+
[500, HEADERS, [body]]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def selected_checks(params)
|
106
|
+
if (selected = params['checks'])
|
107
|
+
selected = selected.split(',').map(&:to_sym)
|
108
|
+
return @checks.values_at(*selected).compact
|
109
|
+
end
|
110
|
+
@checks.values
|
111
|
+
end
|
112
|
+
|
113
|
+
# Add a new check with optional `name`. A `:timeout` option can be
|
114
|
+
# specified in seconds for checks that might take longer than the
|
115
|
+
# one second default. A previously added check with the same name
|
116
|
+
# will be replaced.
|
117
|
+
|
118
|
+
def check(name = :default, options = nil, &block)
|
119
|
+
@checks[name.to_sym] = Check.new(name, options, &block)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Does `value` represent a check failure? This default
|
123
|
+
# implementation returns `true` for any value that is an Exception or false.
|
124
|
+
# Subclasses can override this method for different behavior.
|
125
|
+
|
126
|
+
def failure?(value)
|
127
|
+
value.is_a?(Exception) || value == false
|
128
|
+
end
|
129
|
+
|
130
|
+
# Raise Pinglish::TooLong after `seconds` has elapsed. This default
|
131
|
+
# implementation uses Ruby's built-in Timeout class. Subclasses can
|
132
|
+
# override this method for different behavior, but any new
|
133
|
+
# implementation must raise Pinglish::TooLong when the timeout is
|
134
|
+
# exceeded or override `timeout?` appropriately.
|
135
|
+
|
136
|
+
def timeout(seconds, &block)
|
137
|
+
Timeout.timeout seconds, Pinglish::TooLong, &block
|
138
|
+
end
|
139
|
+
|
140
|
+
# Does `value` represent a check timeout? Returns `true` for any
|
141
|
+
# value that is an instance of Pinglish::TooLong.
|
142
|
+
|
143
|
+
def timeout?(value)
|
144
|
+
value.is_a? Pinglish::TooLong
|
145
|
+
end
|
146
|
+
end
|
data/script/bootstrap
ADDED
data/script/release
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
# Tag and push a release.
|
3
|
+
|
4
|
+
set -e
|
5
|
+
|
6
|
+
# Make sure we're in the project root.
|
7
|
+
|
8
|
+
cd $(dirname "$0")/..
|
9
|
+
|
10
|
+
# Build a new gem archive.
|
11
|
+
|
12
|
+
rm -rf pinglish-*.gem
|
13
|
+
gem build -q pinglish.gemspec
|
14
|
+
|
15
|
+
# Make sure we're on the master branch.
|
16
|
+
|
17
|
+
(git branch | grep -q '* master') || {
|
18
|
+
echo "Only release from the master branch."
|
19
|
+
exit 1
|
20
|
+
}
|
21
|
+
|
22
|
+
# Figure out what version we're releasing.
|
23
|
+
|
24
|
+
tag=v`ls pinglish-*.gem | sed 's/^pinglish-\(.*\)\.gem$/\1/'`
|
25
|
+
|
26
|
+
# Make sure we haven't released this version before.
|
27
|
+
|
28
|
+
git fetch -t origin
|
29
|
+
|
30
|
+
(git tag -l | grep -q "$tag") && {
|
31
|
+
echo "Whoops, there's already a '${tag}' tag."
|
32
|
+
exit 1
|
33
|
+
}
|
34
|
+
|
35
|
+
# Tag it and bag it.
|
36
|
+
|
37
|
+
gem push pinglish-*.gem && git tag "$tag" &&
|
38
|
+
git push origin master && git push origin "$tag"
|
data/script/test
ADDED
data/test/check_test.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class PinglishCheckTest < MiniTest::Unit::TestCase
|
4
|
+
def test_initialize
|
5
|
+
check = Pinglish::Check.new(:db)
|
6
|
+
assert_equal :db, check.name
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_initialize_default_timeout
|
10
|
+
check = Pinglish::Check.new(:db)
|
11
|
+
assert_equal 1, check.timeout
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_initialize_override_timeout
|
15
|
+
check = Pinglish::Check.new(:db, :timeout => 2)
|
16
|
+
assert_equal 2, check.timeout
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_call
|
20
|
+
check = Pinglish::Check.new(:db) { :result_of_block }
|
21
|
+
assert_equal :result_of_block, check.call
|
22
|
+
end
|
23
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,262 @@
|
|
1
|
+
require "helper"
|
2
|
+
require "rack/test"
|
3
|
+
|
4
|
+
class PinglishTest < MiniTest::Unit::TestCase
|
5
|
+
FakeApp = lambda { |env| [200, {}, ["fake"]] }
|
6
|
+
|
7
|
+
def build_app(*args, &block)
|
8
|
+
Rack::Builder.new do |builder|
|
9
|
+
builder.map '/_ping' do
|
10
|
+
run Pinglish.new(*args, &block)
|
11
|
+
end
|
12
|
+
builder.run FakeApp
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_app_that_explodes(*args, &block)
|
17
|
+
Rack::Builder.new do |builder|
|
18
|
+
builder.map '/_ping' do
|
19
|
+
run Pinglish.new(*args, &block)
|
20
|
+
end
|
21
|
+
builder.run lambda { |env| raise "boom" }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_with_non_matching_request_path_and_exception
|
26
|
+
app = build_app_that_explodes
|
27
|
+
session = Rack::Test::Session.new(app)
|
28
|
+
|
29
|
+
assert_raises RuntimeError do
|
30
|
+
session.get "/something"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_with_defaults
|
35
|
+
app = build_app
|
36
|
+
|
37
|
+
session = Rack::Test::Session.new(app)
|
38
|
+
session.get "/_ping"
|
39
|
+
assert_equal 200, session.last_response.status
|
40
|
+
assert_equal "application/json; charset=UTF-8",
|
41
|
+
session.last_response.content_type
|
42
|
+
|
43
|
+
json = JSON.load(session.last_response.body)
|
44
|
+
assert json.key?("now")
|
45
|
+
assert_equal "ok", json["status"]
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_with_good_check
|
49
|
+
app = build_app do |ping|
|
50
|
+
ping.check(:db) { :up_and_at_em }
|
51
|
+
ping.check(:queue) { :pushin_and_poppin }
|
52
|
+
end
|
53
|
+
|
54
|
+
session = Rack::Test::Session.new(app)
|
55
|
+
session.get "/_ping"
|
56
|
+
|
57
|
+
assert_equal "application/json; charset=UTF-8",
|
58
|
+
session.last_response.content_type
|
59
|
+
|
60
|
+
json = JSON.load(session.last_response.body)
|
61
|
+
assert json.key?("now")
|
62
|
+
assert_equal "ok", json["status"]
|
63
|
+
assert_equal "up_and_at_em", json["db"]
|
64
|
+
assert_equal "pushin_and_poppin", json["queue"]
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_with_unnamed_check
|
68
|
+
app = build_app do |ping|
|
69
|
+
ping.check { :yohoho }
|
70
|
+
end
|
71
|
+
|
72
|
+
session = Rack::Test::Session.new(app)
|
73
|
+
session.get "/_ping"
|
74
|
+
|
75
|
+
assert_equal "application/json; charset=UTF-8",
|
76
|
+
session.last_response.content_type
|
77
|
+
|
78
|
+
json = JSON.load(session.last_response.body)
|
79
|
+
assert json.key?("now")
|
80
|
+
assert_equal "ok", json["status"]
|
81
|
+
assert_equal "yohoho", json["default"]
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_with_unnamed_check_that_raises
|
85
|
+
app = build_app do |ping|
|
86
|
+
ping.check { raise "nooooope" }
|
87
|
+
end
|
88
|
+
|
89
|
+
session = Rack::Test::Session.new(app)
|
90
|
+
session.get "/_ping"
|
91
|
+
|
92
|
+
assert_equal "application/json; charset=UTF-8",
|
93
|
+
session.last_response.content_type
|
94
|
+
|
95
|
+
json = JSON.load(session.last_response.body)
|
96
|
+
assert json.key?("now")
|
97
|
+
assert_equal 'failures', json["status"]
|
98
|
+
assert_equal ['default'], json["failures"]
|
99
|
+
assert_equal({
|
100
|
+
'state' => 'error',
|
101
|
+
'exception' => 'RuntimeError',
|
102
|
+
'message' => 'nooooope'
|
103
|
+
}, json["default"])
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
def test_with_check_that_raises
|
108
|
+
app = build_app do |ping|
|
109
|
+
ping.check(:db) { :ok }
|
110
|
+
ping.check(:raise) { raise "nooooope" }
|
111
|
+
end
|
112
|
+
|
113
|
+
session = Rack::Test::Session.new(app)
|
114
|
+
session.get "/_ping"
|
115
|
+
|
116
|
+
assert_equal 503, session.last_response.status
|
117
|
+
assert_equal "application/json; charset=UTF-8",
|
118
|
+
session.last_response.content_type
|
119
|
+
|
120
|
+
json = JSON.load(session.last_response.body)
|
121
|
+
assert json.key?("now")
|
122
|
+
assert_equal "failures", json["status"]
|
123
|
+
assert_equal ['raise'], json["failures"]
|
124
|
+
assert_equal({
|
125
|
+
'state' => 'error',
|
126
|
+
'exception' => 'RuntimeError',
|
127
|
+
'message' => 'nooooope'
|
128
|
+
}, json["raise"])
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_with_check_that_returns_false
|
132
|
+
app = build_app do |ping|
|
133
|
+
ping.check(:db) { :ok }
|
134
|
+
ping.check(:fail) { false }
|
135
|
+
end
|
136
|
+
|
137
|
+
session = Rack::Test::Session.new(app)
|
138
|
+
session.get "/_ping"
|
139
|
+
|
140
|
+
assert_equal 503, session.last_response.status
|
141
|
+
assert_equal "application/json; charset=UTF-8",
|
142
|
+
session.last_response.content_type
|
143
|
+
|
144
|
+
json = JSON.load(session.last_response.body)
|
145
|
+
assert json.key?("now")
|
146
|
+
assert_equal "failures", json["status"]
|
147
|
+
assert_equal ["fail"], json["failures"]
|
148
|
+
assert_equal false, json.key?("fail")
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_with_check_that_times_out
|
152
|
+
app = build_app do |ping|
|
153
|
+
ping.check(:db) { :ok }
|
154
|
+
ping.check(:long, :timeout => 0.001) { sleep 0.003 }
|
155
|
+
end
|
156
|
+
|
157
|
+
session = Rack::Test::Session.new(app)
|
158
|
+
session.get "/_ping"
|
159
|
+
|
160
|
+
assert_equal 503, session.last_response.status
|
161
|
+
assert_equal "application/json; charset=UTF-8",
|
162
|
+
session.last_response.content_type
|
163
|
+
|
164
|
+
json = JSON.load(session.last_response.body)
|
165
|
+
assert json.key?("now")
|
166
|
+
assert_equal "failures", json["status"]
|
167
|
+
assert_equal ["long"], json["timeouts"]
|
168
|
+
end
|
169
|
+
|
170
|
+
def test_with_checks_taking_more_than_max
|
171
|
+
app = build_app(:max => 0.001) do |ping|
|
172
|
+
ping.check(:long) { sleep 0.003 }
|
173
|
+
end
|
174
|
+
|
175
|
+
session = Rack::Test::Session.new(app)
|
176
|
+
session.get "/_ping"
|
177
|
+
|
178
|
+
assert_equal 503, session.last_response.status
|
179
|
+
assert_equal "application/json; charset=UTF-8",
|
180
|
+
session.last_response.content_type
|
181
|
+
|
182
|
+
json = JSON.load(session.last_response.body)
|
183
|
+
assert json.key?("now")
|
184
|
+
assert_equal "failures", json["status"]
|
185
|
+
assert_equal ['long'], json["timeouts"]
|
186
|
+
end
|
187
|
+
|
188
|
+
def test_with_script_name
|
189
|
+
app = build_app
|
190
|
+
|
191
|
+
session = Rack::Test::Session.new(app)
|
192
|
+
session.get "/_ping", {}, "SCRIPT_NAME" => "/myapp"
|
193
|
+
assert_equal 200, session.last_response.status
|
194
|
+
assert_equal "application/json; charset=UTF-8",
|
195
|
+
session.last_response.content_type
|
196
|
+
|
197
|
+
json = JSON.load(session.last_response.body)
|
198
|
+
assert json.key?("now")
|
199
|
+
assert_equal "ok", json["status"]
|
200
|
+
end
|
201
|
+
|
202
|
+
def test_with_selective_checks
|
203
|
+
app = build_app do |ping|
|
204
|
+
ping.check(:db) { :ok }
|
205
|
+
ping.check(:foo) { :bar }
|
206
|
+
ping.check(:long, :timeout => 0.001) { sleep 0.003 }
|
207
|
+
end
|
208
|
+
|
209
|
+
session = Rack::Test::Session.new(app)
|
210
|
+
session.get "/_ping?checks=db,foo"
|
211
|
+
|
212
|
+
assert_equal 200, session.last_response.status
|
213
|
+
assert_equal "application/json; charset=UTF-8",
|
214
|
+
session.last_response.content_type
|
215
|
+
|
216
|
+
json = JSON.load(session.last_response.body)
|
217
|
+
assert json.key?("now")
|
218
|
+
assert_equal "ok", json["status"]
|
219
|
+
assert_equal false, json.key?("timeouts")
|
220
|
+
assert_equal false, json.key?("failures")
|
221
|
+
assert_equal 'ok', json['db']
|
222
|
+
assert_equal 'bar', json['foo']
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_check_without_name
|
226
|
+
pinglish = Pinglish.new(FakeApp)
|
227
|
+
check = pinglish.check { :ok }
|
228
|
+
assert_instance_of Pinglish::Check, check
|
229
|
+
end
|
230
|
+
|
231
|
+
def test_check_with_name
|
232
|
+
pinglish = Pinglish.new(FakeApp)
|
233
|
+
check = pinglish.check(:db) { :ok }
|
234
|
+
assert_instance_of Pinglish::Check, check
|
235
|
+
assert_equal :db, check.name
|
236
|
+
end
|
237
|
+
|
238
|
+
def test_failure_boolean
|
239
|
+
pinglish = Pinglish.new(FakeApp)
|
240
|
+
|
241
|
+
assert pinglish.failure?(Exception.new)
|
242
|
+
assert pinglish.failure?(false)
|
243
|
+
|
244
|
+
refute pinglish.failure?(true)
|
245
|
+
refute pinglish.failure?(:ok)
|
246
|
+
end
|
247
|
+
|
248
|
+
def test_timeout
|
249
|
+
pinglish = Pinglish.new(FakeApp)
|
250
|
+
|
251
|
+
assert_raises Pinglish::TooLong do
|
252
|
+
pinglish.timeout(0.001) { sleep 0.003 }
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def test_timeout_boolean
|
257
|
+
pinglish = Pinglish.new(FakeApp)
|
258
|
+
|
259
|
+
assert pinglish.timeout?(Pinglish::TooLong.new)
|
260
|
+
refute pinglish.timeout?(Exception.new)
|
261
|
+
end
|
262
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: goodguide-pinglish
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John Barnette
|
8
|
+
- Will Farrington
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-04-25 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rack
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: minitest
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '4.5'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '4.5'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rack-test
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0.6'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0.6'
|
56
|
+
description: A simple Rack middleware for checking app health.
|
57
|
+
email:
|
58
|
+
- jbarnette@github.com
|
59
|
+
- wfarr@github.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- ".gitignore"
|
65
|
+
- Gemfile
|
66
|
+
- LICENSE
|
67
|
+
- README.md
|
68
|
+
- goodguide-pinglish.gemspec
|
69
|
+
- lib/pinglish.rb
|
70
|
+
- lib/pinglish/check.rb
|
71
|
+
- script/bootstrap
|
72
|
+
- script/release
|
73
|
+
- script/test
|
74
|
+
- test/check_test.rb
|
75
|
+
- test/helper.rb
|
76
|
+
- test/pinglish_test.rb
|
77
|
+
homepage: https://github.com/goodguide/pinglish
|
78
|
+
licenses: []
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.2.2
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: "/_ping your way to freedom."
|
100
|
+
test_files:
|
101
|
+
- test/check_test.rb
|
102
|
+
- test/helper.rb
|
103
|
+
- test/pinglish_test.rb
|
104
|
+
has_rdoc:
|