pinglish 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ /*.gem
2
+ /.bundle
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
@@ -0,0 +1,18 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pinglish (0.0.0)
5
+ rack
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ minitest (4.5.0)
11
+ rack (1.5.0)
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ minitest (~> 4.5)
18
+ pinglish!
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.
@@ -0,0 +1,124 @@
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 Reds," 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__ return an `HTTP 418 I'M A TEAPOT` status code
26
+ if the request asks for any content-type but `application/json`.
27
+
28
+ 0. The response __must__ be of Content-Type `application/json;
29
+ charset=UTF-8`.
30
+
31
+ 0. The response __must__ be valid JSON no matter what, even if JSON
32
+ serialization or other fundamental code fails.
33
+
34
+ 0. The response __must__ contain a `"status"` key set either to `"ok"`
35
+ or `"fail"`.
36
+
37
+ 0. The response __must__ contain a `"now"` key set to the current
38
+ server's time in seconds since epoch as a string.
39
+
40
+ 0. If the `"status"` key is set to `"fail"`, the response __may__
41
+ contain a `"failures"` key set to an Array of string names
42
+ representing failed checks.
43
+
44
+ 0. If the `"status"` key is set to `"fail"`, the response __may__
45
+ contain a `"timeouts"` key set to an Array of string names
46
+ representing checks that exceeded an implementation-specific
47
+ individual timeout.
48
+
49
+ 0. The response body __may__ contain any other top-level keys to
50
+ supply additional data about services the application consumes, but
51
+ all values must be strings, arrays of strings, or hashes where both
52
+ keys and values are strings.
53
+
54
+ ### An Example Response
55
+
56
+ ```javascript
57
+ {
58
+
59
+ // These two keys will always exist.
60
+
61
+ "now": "1359055102",
62
+ "status": "fail",
63
+
64
+ // This key may only exist when a named check has failed.
65
+
66
+ "failures": ["db"],
67
+
68
+ // This key may only exist when a named check exceeds its timeout.
69
+
70
+ "timeouts": ["really-long-check"],
71
+
72
+ // Keys like this may exist to provide extra information about
73
+ // healthy services, like the number of objects in an S3 bucket.
74
+
75
+ "s3": "127"
76
+ }
77
+ ```
78
+
79
+ ## The Middleware
80
+
81
+ FIX: exegesis
82
+
83
+ ```ruby
84
+ require "pinglish"
85
+
86
+ use Pinglish do |ping|
87
+
88
+ # A single unnamed check is the simplest possible way to use
89
+ # Pinglish, and you'll probably never want combine it with other
90
+ # named checks. An unnamed check contributes to overall success or
91
+ # failure, but never adds additional data to the response.
92
+
93
+ ping.check do
94
+ App.healthy?
95
+ end
96
+
97
+ # A named check like this can provide useful summary information
98
+ # when it succeeds. In this case, a top-level "db" key will appear
99
+ # in the response containing the number of items in the database. If
100
+ # a check returns nil, no key will be added to the response.
101
+
102
+ ping.check :db do
103
+ App.db.items.size
104
+ end
105
+
106
+ # By default, checks time out after one second. You can override
107
+ # this with the :timeout option, but be aware that no combination of
108
+ # checks is ever allowed to exceed the overall 29 second limit.
109
+
110
+ ping.check :long, :timeout => 5 do
111
+ App.dawdle
112
+ end
113
+
114
+ # Signal check failure by raising an exception.
115
+
116
+ ping.check :fails do
117
+ false or raise "Everything's ruined."
118
+ end
119
+ end
120
+ ```
121
+
122
+ ## Contributing
123
+
124
+ FIX
@@ -0,0 +1,161 @@
1
+ require "json"
2
+ require "timeout"
3
+
4
+ # This Rack middleware provides a "/_ping" endpoint for configurable
5
+ # system health checks. It's intended to be consumed by machines.
6
+
7
+ class Pinglish
8
+
9
+ # The HTTP headers sent for every response.
10
+
11
+ HEADERS = {
12
+ "Content-Type" => "application/json; charset=UTF-8"
13
+ }
14
+
15
+ # The maximum amount of time for all checks. 29 seconds, to come in
16
+ # just under the 30 second limit of many monitoring services.
17
+
18
+ MAX_TOTAL_TIME = 29
19
+
20
+ # This triple is returned if the request media type isn't
21
+ # "application/json".
22
+
23
+ TEAPOT = [418, HEADERS, ['{"teapot":"true"}']]
24
+
25
+ # Represents a check, which is a behavior block with a name and
26
+ # timeout in seconds.
27
+
28
+ class Check
29
+ attr_reader :name
30
+ attr_reader :timeout
31
+
32
+ def initialize(name, options = nil, &block)
33
+ @name = name
34
+ @timeout = options && options[:timeout] || 1
35
+ @block = block
36
+ end
37
+
38
+ # Call this check's behavior, returning the result of the block.
39
+
40
+ def call(*args, &block)
41
+ @block.call *args, &block
42
+ end
43
+ end
44
+
45
+ # Raised when a check exceeds its timeout.
46
+
47
+ class TooLong < RuntimeError; end
48
+
49
+ # Create a new instance of the middleware wrapping `app`, with an
50
+ # optional `path` (the default is `"/_ping"`) and behavior `block`.
51
+
52
+ def initialize(app, path = nil, &block)
53
+ @app = app
54
+ @checks = {}
55
+ @path = path || "/_ping"
56
+
57
+ yield self if block_given?
58
+ end
59
+
60
+ # Exercise the middleware, immediately delegating to the wrapped app
61
+ # unless the request path matches `path`.
62
+
63
+ def call(env)
64
+ request = Rack::Request.new env
65
+
66
+ return @app.call env unless request.path == @path
67
+ return TEAPOT unless request.media_type == "application/json"
68
+
69
+ timeout MAX_TOTAL_TIME do
70
+ results = {}
71
+
72
+ @checks.values.each do |check|
73
+ begin
74
+ timeout check.timeout do
75
+ results[check.name] = check.call
76
+ end
77
+ rescue StandardError => e
78
+ results[check.name] = e
79
+ end
80
+ end
81
+
82
+ failed = results.values.any? { |v| failure? v }
83
+ http_status = failed ? 503 : 200
84
+ text_status = failed ? "fail" : "ok"
85
+
86
+ data = {
87
+ :now => Time.now.to_i.to_s,
88
+ :status => text_status
89
+ }
90
+
91
+ results.each do |name, value|
92
+
93
+ # The unnnamed/default check doesn't contribute data.
94
+ next if name.nil?
95
+
96
+ if failure? value
97
+
98
+ # If a check fails its name is added to a `failures` array.
99
+ # If the check failed because it timed out, its name is
100
+ # added to a `timeouts` array instead.
101
+
102
+ key = timeout?(value) ? :timeouts : :failures
103
+ (data[key] ||= []) << name
104
+
105
+ elsif value
106
+
107
+ # If the check passed and returned a value, the stringified
108
+ # version of the value is returned under the `name` key.
109
+
110
+ data[name] = value.to_s
111
+ end
112
+ end
113
+
114
+ [http_status, HEADERS, [JSON.generate(data)]]
115
+ end
116
+
117
+ rescue Exception => ex
118
+
119
+ p :fuck => ex
120
+ # Something catastrophic happened. We can't even run the checks
121
+ # and render a JSON response. Fall back on a pre-rendered string
122
+ # and interpolate the current epoch time.
123
+
124
+ now = Time.now.to_i.to_s
125
+ [500, HEADERS, ['{"status":"fail","now":"' + now + '"}']]
126
+ end
127
+
128
+ # Add a new check with optional `name`. A `:timeout` option can be
129
+ # specified in seconds for checks that might take longer than the
130
+ # one second default. A previously added check with the same name
131
+ # will be replaced.
132
+
133
+ def check(name = nil, options = nil, &block)
134
+ @checks[name] = Check.new(name, options, &block)
135
+ end
136
+
137
+ # Does `value` represent a check failure? This default
138
+ # implementation returns `true` for any value that is an Exception.
139
+ # Subclasses can override this method for different behavior.
140
+
141
+ def failure?(value)
142
+ value.is_a? Exception
143
+ end
144
+
145
+ # Raise Pinglish::TooLong after `seconds` has elapsed. This default
146
+ # implementation uses Ruby's built-in Timeout class. Subclasses can
147
+ # override this method for different behavior, but any new
148
+ # implementation must raise Pinglish::TooLong when the timeout is
149
+ # exceeded or override `timeout?` appropriately.
150
+
151
+ def timeout(seconds, &block)
152
+ Timeout.timeout seconds, Pinglish::TooLong, &block
153
+ end
154
+
155
+ # Does `value` represent a check timeout? Returns `true` for any
156
+ # value that is an instance of Pinglish::TooLong.
157
+
158
+ def timeout?(value)
159
+ value.is_a? Pinglish::TooLong
160
+ end
161
+ end
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = "pinglish"
5
+ gem.version = "0.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/jbarnette/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
+ end
@@ -0,0 +1,9 @@
1
+ #!/bin/sh
2
+ # Make sure local development dependencies are satisfied.
3
+
4
+ set -e
5
+
6
+ cd $(dirname "$0")/..
7
+ rm -rf .bundle/config
8
+
9
+ bundle install --path .bundle --binstubs .bundle/binstubs --quiet
@@ -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"
@@ -0,0 +1,9 @@
1
+ #!/bin/sh
2
+ # Run the tests.
3
+
4
+ set -e
5
+
6
+ cd $(dirname "$0")/..
7
+
8
+ script/bootstrap && ruby -I lib -rubygems \
9
+ -e 'require "bundler/setup"; Dir["test/**/*_test.rb"].each { |f| load f }' "$@"
@@ -0,0 +1,7 @@
1
+ require "minitest/autorun"
2
+
3
+ class PinglishTest < MiniTest::Unit::TestCase
4
+ def test_sanity
5
+ assert true
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pinglish
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - John Barnette
9
+ - Will Farrington
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-01-24 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rack
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: minitest
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ~>
37
+ - !ruby/object:Gem::Version
38
+ version: '4.5'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: '4.5'
47
+ description: A simple Rack middleware for checking app health.
48
+ email:
49
+ - jbarnette@github.com
50
+ - wfarr@github.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - .gitignore
56
+ - Gemfile
57
+ - Gemfile.lock
58
+ - LICENSE
59
+ - README.md
60
+ - lib/pinglish.rb
61
+ - pinglish.gemspec
62
+ - script/bootstrap
63
+ - script/release
64
+ - script/tests
65
+ - test/pinglish_test.rb
66
+ homepage: https://github.com/jbarnette/pinglish
67
+ licenses: []
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 1.8.23
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: /_ping your way to freedom.
90
+ test_files:
91
+ - test/pinglish_test.rb