rapporteur 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 +15 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.travis.yml +14 -0
- data/Appraisals +7 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +22 -0
- data/README.md +219 -0
- data/Rakefile +6 -0
- data/app/controllers/statuses_controller.rb +10 -0
- data/config/locales/en.yml +8 -0
- data/config/routes.rb +3 -0
- data/config.ru +7 -0
- data/gemfiles/rails3.1.gemfile +7 -0
- data/gemfiles/rails3.2.gemfile +7 -0
- data/lib/rapporteur/checker.rb +111 -0
- data/lib/rapporteur/checks/active_record_check.rb +11 -0
- data/lib/rapporteur/checks.rb +5 -0
- data/lib/rapporteur/engine.rb +4 -0
- data/lib/rapporteur/responder.rb +32 -0
- data/lib/rapporteur/revision.rb +56 -0
- data/lib/rapporteur/rspec.rb +68 -0
- data/lib/rapporteur/serializer.rb +20 -0
- data/lib/rapporteur/version.rb +3 -0
- data/lib/rapporteur.rb +15 -0
- data/rapporteur.gemspec +31 -0
- data/spec/internal/config/database.yml +3 -0
- data/spec/internal/config/routes.rb +3 -0
- data/spec/internal/db/schema.rb +3 -0
- data/spec/internal/log/.gitignore +1 -0
- data/spec/internal/public/favicon.ico +0 -0
- data/spec/requests/active_record_check_spec.rb +26 -0
- data/spec/requests/no_checks_spec.rb +9 -0
- data/spec/routing/routes_spec.rb +23 -0
- data/spec/spec_helper.rb +19 -0
- metadata +215 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NDgwNmQ2Yjc2Yzc3Y2NhMTk5YmMwMmFjNWNhZTQ3YWY2NGM0OTFjNw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NDg0Njg3NTBiZmYyZmM0ODVjOGY0ZTIzYWE5MTcyMmI0M2U4ZjJjOQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NTE4NWFlODVmMjcxZjg3YTExZGIxMDQyNmI1OWM3ZmQxMGU5MzgyMmVjNDlk
|
10
|
+
MjMyYzE2NGYzMTQ0YzdjM2Q5ZTNhNmMzNGEyOTUwZDM5NjAyYzU2Y2ExNzY5
|
11
|
+
ZjQ5YTlmZWRiNWZhODQ4YjcxNzJkOTlmNWMxOWExODNiMTdiYzQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YjM5OTIzNjM0MDI5ODU1YjA3ZTc2ZDYwM2RlYWIwYTIzODljMmViY2YwNzg5
|
14
|
+
YThmMThkMzExMzAxOGZkMWU1YmQxMmE0NTMzNTg4N2E3M2ZlOTMxYTU3OGY5
|
15
|
+
MWIwNjUwNTJlODQ2OTAyZGU5NWNjYjBlNjZkMmM1ODMyMDU4Nzg=
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
.rvmrc
|
2
|
+
.ruby-version
|
3
|
+
.ruby-gemset
|
4
|
+
*.gem
|
5
|
+
*.rbc
|
6
|
+
.bundle
|
7
|
+
.config
|
8
|
+
.yardoc
|
9
|
+
Gemfile.lock
|
10
|
+
*.gemfile.lock
|
11
|
+
InstalledFiles
|
12
|
+
_yardoc
|
13
|
+
coverage
|
14
|
+
doc/
|
15
|
+
lib/bundler/man
|
16
|
+
pkg
|
17
|
+
rdoc
|
18
|
+
spec/reports
|
19
|
+
test/tmp
|
20
|
+
test/version_tmp
|
21
|
+
tmp
|
22
|
+
*.sqlite
|
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.9.2
|
4
|
+
- 1.9.3
|
5
|
+
gemfile:
|
6
|
+
- gemfiles/rails3.1.gemfile
|
7
|
+
- gemfiles/rails3.2.gemfile
|
8
|
+
notifications:
|
9
|
+
email: false
|
10
|
+
campfire:
|
11
|
+
on_success: change
|
12
|
+
on_failure: always
|
13
|
+
rooms:
|
14
|
+
- secure: "gxEt3SeVn1kup6PfB6hiQu6eWsefmMEdd8U1qPSS6vlRjsM7Xy2IrXdz9gdl\n0nrrRgESvOxT3sqMh4/opH6M1kbtCyl3M0yvjF2QUtjWQ+4BStJGhNyXlDTp\nNAas19fuEUPBNxNqoy7aTVBiFpQRs0NisEZTS3+N3eA2tz757Qk="
|
data/Appraisals
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Envy Labs LLC and Code School LLC
|
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,219 @@
|
|
1
|
+
# Rapporteur
|
2
|
+
|
3
|
+
This gem provides a singular, status-checking endpoint to your application. The
|
4
|
+
endpoint provides a JSON response with either an HTTP 200 or an HTTP 500
|
5
|
+
response, depending on the current application environment.
|
6
|
+
|
7
|
+
When the environment tests successfully, an HTTP 200 response is returned with
|
8
|
+
the current application Git revision and server time:
|
9
|
+
|
10
|
+
```json
|
11
|
+
{
|
12
|
+
"revision": "906731e6467ea381ba5bc70f103b85ed4178fee7",
|
13
|
+
"time": "2013-05-19T05:38:46Z"
|
14
|
+
}
|
15
|
+
```
|
16
|
+
|
17
|
+
When an application validation fails, an HTTP 500 response is returned with a
|
18
|
+
collection of error messages, similar to the default Rails responders for model
|
19
|
+
validations:
|
20
|
+
|
21
|
+
```json
|
22
|
+
{
|
23
|
+
"errors": {
|
24
|
+
"base": ["The application database is inaccessible or unavailable"]
|
25
|
+
}
|
26
|
+
}
|
27
|
+
```
|
28
|
+
|
29
|
+
## Installation
|
30
|
+
|
31
|
+
Supported Ruby versions:
|
32
|
+
|
33
|
+
* MRI 1.9.2
|
34
|
+
* MRI 1.9.3
|
35
|
+
|
36
|
+
Supported Rails versions:
|
37
|
+
|
38
|
+
* Rails 3.1.x.
|
39
|
+
* Rails 3.2.x.
|
40
|
+
|
41
|
+
To install, add this line to your application's Gemfile:
|
42
|
+
|
43
|
+
gem 'rapporteur'
|
44
|
+
|
45
|
+
And then execute:
|
46
|
+
|
47
|
+
$ bundle
|
48
|
+
|
49
|
+
## Usage
|
50
|
+
|
51
|
+
Simply adding the gem requirement to your Gemfile is enough to install and
|
52
|
+
automatically load and configure the gem. Technically speaking, the gem is a
|
53
|
+
Rails Engine, so it auto-initializes with Rails starts up. There is no further
|
54
|
+
configuration necessary.
|
55
|
+
|
56
|
+
By default, there are no application checks that run and the status endpoint
|
57
|
+
simply reports the current application revision and time. This is useful for a
|
58
|
+
basic connectivity check to be watched by a third party service like Pingdom
|
59
|
+
for a very simple, non-critical application.
|
60
|
+
|
61
|
+
You may optionally use any of the pre-defined checks (such as the ActiveRecord
|
62
|
+
connection check) to expand the robustness of the status checks. Adding a check
|
63
|
+
will execute that check each time the status endpoint is requested, so be
|
64
|
+
somewhat wary of doing _too_ much. See more in the [Adding checks
|
65
|
+
section](#adding-checks), below.
|
66
|
+
|
67
|
+
Further, you can define your own checks which could be custom to your
|
68
|
+
application or environment and report their own, unique errors. The only
|
69
|
+
requirement is that the check objects are callable (respond to `#call`, like a
|
70
|
+
Proc). See more in the [Creating custom checks
|
71
|
+
section](#creating-custom-checks), below.
|
72
|
+
|
73
|
+
### The endpoint
|
74
|
+
|
75
|
+
This gem provides a new, single endpoint in your application. Specifically, it
|
76
|
+
creates a named `/status.json` route, with the "status" name. It does not match
|
77
|
+
on any other format or variation, which isolates the pollution of your
|
78
|
+
application routes.
|
79
|
+
|
80
|
+
If you'd like to link to the status endpoint from within your application (why,
|
81
|
+
I couldn't guess), you can use a standard Rails URL helper:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
link_to status_path
|
85
|
+
```
|
86
|
+
|
87
|
+
Were you already using the `/status.json` endpoint or the "status" route name?
|
88
|
+
Hmm. Well... you just broke it.
|
89
|
+
|
90
|
+
## Customization
|
91
|
+
|
92
|
+
### Adding checks
|
93
|
+
|
94
|
+
This gem ships with the following checks tested and packaged:
|
95
|
+
|
96
|
+
* **Rapporteur::Checks::ActiveRecordCheck** - Performs a trivial test
|
97
|
+
of the current `ActiveRecord::Base.connection` to ensure basic database
|
98
|
+
connectivity.
|
99
|
+
|
100
|
+
To add checks to your application, define the checks you'd like to run in your
|
101
|
+
environment or application configuration files or initializers, such as:
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
# config/initializers/rapporteur.rb
|
105
|
+
Rapporteur::Checker.add_check(Rapporteur::Checks::ActiveRecordCheck)
|
106
|
+
```
|
107
|
+
|
108
|
+
Or, make an environment specific check with:
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
# config/environments/production.rb
|
112
|
+
MyApplication.configure do
|
113
|
+
config.to_prepare do
|
114
|
+
Rapporteur::Checker.add_check(Rapporteur::Checks::ActiveRecordCheck)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
### Creating custom checks
|
120
|
+
|
121
|
+
It is simple to add a custom check to the status endpoint. All that is required
|
122
|
+
is that you give the checker an object that is callable. In your object, simply
|
123
|
+
check for the state of the world that you're interested in, and if you're not
|
124
|
+
happy with it, add an error to the given `checker` instance:
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
# config/initializers/rapporteur.rb
|
128
|
+
|
129
|
+
my_proc_check = lambda { |checker|
|
130
|
+
checker.add_error("You have bad luck!") if rand(10) > 5
|
131
|
+
}
|
132
|
+
|
133
|
+
Rapporteur::Checker.add_check(my_proc_check)
|
134
|
+
|
135
|
+
class MyClassCheck
|
136
|
+
def self.call(checker)
|
137
|
+
@@counter ||= 0
|
138
|
+
checker.add_error("Stop calling me!!") if @@counter > 50
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
Rapporteur::Checker.add_check(MyClassCheck)
|
143
|
+
```
|
144
|
+
|
145
|
+
Certainly, the definition and registration of the checks do not need to occur
|
146
|
+
within the same file, but you get the idea. Also: Please make your checks more
|
147
|
+
useful than those defined above. ;)
|
148
|
+
|
149
|
+
You could create a checker for your active Redis connection, Memcached
|
150
|
+
connections, disk usage percentage, process count, memory usage, or really
|
151
|
+
anything you like. Again, because these checks get executed every time the
|
152
|
+
status endpoint is called, **be mindful of the tradeoffs when making a check that
|
153
|
+
may be resource intensive**.
|
154
|
+
|
155
|
+
### Customizing the revision
|
156
|
+
|
157
|
+
If you need to customize the way in which the current application revision is
|
158
|
+
calculated (by default it runs a `git rev-parse HEAD`), you may do so by
|
159
|
+
modifying the necessary environment file or creating an initializer in your
|
160
|
+
Rails application:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
# config/initializers/rapporteur.rb
|
164
|
+
Rapporteur::Revision.current = "revision123"
|
165
|
+
```
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
# config/environments/production.rb
|
169
|
+
MyApplication.configure do
|
170
|
+
config.to_prepare do
|
171
|
+
Rapporteur::Revision.current = "revision123"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
```
|
175
|
+
|
176
|
+
You may pass a String or a callable object (Proc) to `.current=` and it will be
|
177
|
+
executed and memoized. Useful examples of this are:
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
# Read a Capistrano REVISION file
|
181
|
+
Rapporteur::Revision.current = Rails.root.join("REVISION").read.strip
|
182
|
+
|
183
|
+
# Force a particular directory and use Git
|
184
|
+
Rapporteur::Revision.current = `cd "#{Rails.root}" && git rev-parse HEAD`.strip
|
185
|
+
|
186
|
+
# Use an ENV variable (Heroku)
|
187
|
+
Rapporteur::Revision.current = ENV["REVISION"]
|
188
|
+
|
189
|
+
# Do some crazy calculation
|
190
|
+
Rapporteur::Revision.current = lambda { MyRevisionCalculator.execute! }
|
191
|
+
```
|
192
|
+
|
193
|
+
### Customizing the error messages
|
194
|
+
|
195
|
+
The error messages displayed in the event that application validations fail are
|
196
|
+
all collected through I18n. There are default localization strings provided
|
197
|
+
with the gem, but you may override them as necessary, simply by redefining them
|
198
|
+
in a locales file within your local application.
|
199
|
+
|
200
|
+
For example, to override the database check failure message:
|
201
|
+
|
202
|
+
```yaml
|
203
|
+
en:
|
204
|
+
activemodel:
|
205
|
+
errors:
|
206
|
+
models:
|
207
|
+
rapporteur/checker:
|
208
|
+
attributes:
|
209
|
+
base:
|
210
|
+
database_unavailable: "Something went wrong"
|
211
|
+
```
|
212
|
+
|
213
|
+
## Contributing
|
214
|
+
|
215
|
+
1. Fork it
|
216
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
217
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
218
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
219
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/config/routes.rb
ADDED
data/config.ru
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module Rapporteur
|
5
|
+
# The center of the Rapporteur library, Checker manages holding and running
|
6
|
+
# the custom checks, holding any application error messages, and provides the
|
7
|
+
# controller with that data for rendering.
|
8
|
+
#
|
9
|
+
class Checker
|
10
|
+
include Singleton
|
11
|
+
include ActiveModel::Validations
|
12
|
+
include ActiveModel::SerializerSupport
|
13
|
+
|
14
|
+
|
15
|
+
# Public: Add a pre-built or custom check to your status endpoint. These
|
16
|
+
# checks are used to test the state of the world of the application, and
|
17
|
+
# need only respond to `#call`.
|
18
|
+
#
|
19
|
+
# Once added, the given check will be called and passed an instance of this
|
20
|
+
# checker. If everything is good, do nothing! If there is a problem, use
|
21
|
+
# `add_error` to add an error message to the checker.
|
22
|
+
#
|
23
|
+
# Examples
|
24
|
+
#
|
25
|
+
# Rapporteur::Checker.add_check(lambda { |checker|
|
26
|
+
# checker.add_error("Bad luck.") if rand(2) == 1
|
27
|
+
# })
|
28
|
+
#
|
29
|
+
# Returns Rapporteur::Checker.
|
30
|
+
# Raises ArgumentError if the given check does not respond to call.
|
31
|
+
#
|
32
|
+
def self.add_check(object)
|
33
|
+
raise ArgumentError, "A check must respond to #call." unless object.respond_to?(:call)
|
34
|
+
instance.checks << object
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
# Public: Empties all configured checks from the checker. This may be
|
39
|
+
# useful for testing and for cases where you might've built up some basic
|
40
|
+
# checks but for one reason or another (environment constraint) need to
|
41
|
+
# start from scratch.
|
42
|
+
#
|
43
|
+
# Returns Rapporteur::Checker.
|
44
|
+
#
|
45
|
+
def self.clear
|
46
|
+
instance.checks.clear
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
# Public: This is the primary execution point for this class. Use run to
|
51
|
+
# exercise the configured checker and collect any application errors or
|
52
|
+
# data for rendering.
|
53
|
+
#
|
54
|
+
# Returns a Rapporteur::Checker instance.
|
55
|
+
#
|
56
|
+
def self.run
|
57
|
+
instance.errors.clear
|
58
|
+
instance.run
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
# Public: Add an error message to the checker in order to have it rendered
|
63
|
+
# in the status request.
|
64
|
+
#
|
65
|
+
# It is suggested that you use I18n and locale files for these messages, as
|
66
|
+
# is done with the pre-built checks. If you're using I19n, you'll need to
|
67
|
+
# define `activemodel.errors.models.rapporteur/checker.attributes.base.<your key>`.
|
68
|
+
#
|
69
|
+
# Examples
|
70
|
+
#
|
71
|
+
# checker.add_error("You failed.")
|
72
|
+
# checker.add_error(:i18n_key_is_better)
|
73
|
+
#
|
74
|
+
# Returns the Rapporteur::Checker instance.
|
75
|
+
#
|
76
|
+
def add_error(message)
|
77
|
+
errors.add(:base, message)
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
# Public: Returns the Set of checks currently configured.
|
82
|
+
#
|
83
|
+
def checks
|
84
|
+
@checks ||= Set.new
|
85
|
+
end
|
86
|
+
|
87
|
+
# Public: Returns a String containing the current revision of the
|
88
|
+
# application.
|
89
|
+
#
|
90
|
+
def revision
|
91
|
+
Revision.current
|
92
|
+
end
|
93
|
+
|
94
|
+
# Public: Executes the configured checks.
|
95
|
+
#
|
96
|
+
# Returns the Rapporteur::Checker instance.
|
97
|
+
#
|
98
|
+
def run
|
99
|
+
checks.each do |object|
|
100
|
+
object.call(self)
|
101
|
+
end
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
# Public: Returns a Time instance containing the current system time.
|
106
|
+
#
|
107
|
+
def time
|
108
|
+
Time.now
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Rapporteur
|
2
|
+
# A customization of the default Rails ActionController::Responder.
|
3
|
+
# Primarily, this is used to smooth out the differences between Rails
|
4
|
+
# responder versions and allow for error messages in GET requests.
|
5
|
+
#
|
6
|
+
class Responder < ActionController::Responder
|
7
|
+
# Internal: Overrides the default behavior by ignoring the HTTP verb and
|
8
|
+
# always responding with errors if the rendering resource contains errors.
|
9
|
+
#
|
10
|
+
def to_format
|
11
|
+
if has_errors?
|
12
|
+
display_errors
|
13
|
+
else
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
|
22
|
+
if Rails::VERSION::MAJOR == 3 && Rails::VERSION::MINOR == 1
|
23
|
+
def display_errors
|
24
|
+
controller.render format => {errors: resource.errors}, status: :internal_server_error
|
25
|
+
end
|
26
|
+
elsif Rails::VERSION::MAJOR == 3 && Rails::VERSION::MINOR == 2
|
27
|
+
def display_errors
|
28
|
+
controller.render format => resource_errors, status: :internal_server_error
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Rapporteur
|
2
|
+
# Manages memoizing and maintaining the current application revision.
|
3
|
+
#
|
4
|
+
class Revision
|
5
|
+
class_attribute :_current
|
6
|
+
|
7
|
+
# Public: Returns the current revision as a String.
|
8
|
+
#
|
9
|
+
def self.current
|
10
|
+
self._current ||= calculate_current
|
11
|
+
end
|
12
|
+
|
13
|
+
# Public: Forcibly sets the current application revision.
|
14
|
+
#
|
15
|
+
# revision - Either a String or a callable object (Proc, for example) to
|
16
|
+
# use your own environment logic to determine the revision.
|
17
|
+
#
|
18
|
+
# Examples
|
19
|
+
#
|
20
|
+
# Rapporteur::Revision.current = ENV['REVISION'].strip
|
21
|
+
# Rapporteur::Revision.current = Rails.root.join("REVISION").read.strip
|
22
|
+
#
|
23
|
+
# Returns the revision given.
|
24
|
+
#
|
25
|
+
def self.current=(revision)
|
26
|
+
self._current = calculate_current(revision)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Internal: The default method of determining the current revision. This
|
30
|
+
# assumes a git executable is in the current PATH and that the process
|
31
|
+
# context is running the the appropriate git application directory.
|
32
|
+
#
|
33
|
+
# Returns a String containing the current git revision, hopefully.
|
34
|
+
#
|
35
|
+
def self.default_revision_source
|
36
|
+
`git rev-parse HEAD 2>/dev/null`.strip
|
37
|
+
rescue
|
38
|
+
end
|
39
|
+
|
40
|
+
# Internal: Calculates the current revision from the configured revision
|
41
|
+
# source.
|
42
|
+
#
|
43
|
+
def self.calculate_current(revision = default_revision_source)
|
44
|
+
case revision
|
45
|
+
when String
|
46
|
+
revision
|
47
|
+
when Proc
|
48
|
+
revision.call.to_s
|
49
|
+
when NilClass
|
50
|
+
"You must provide a Rapporteur::Revision.current= String or Proc"
|
51
|
+
else
|
52
|
+
raise ArgumentError, "Unknown revision type given: #{revision.inspect}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'rapporteur'
|
2
|
+
|
3
|
+
shared_examples_for 'a successful status response' do
|
4
|
+
it 'responds with HTTP 200' do
|
5
|
+
expect(subject.response_code).to(eq(200))
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'responds with a JSON content header' do
|
9
|
+
expect(subject.content_type).to(eq(Mime::JSON))
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'responds with valid JSON' do
|
13
|
+
expect { JSON.parse(subject.body) }.not_to(raise_error)
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'the response payload' do
|
17
|
+
subject { get(status_path) ; JSON.parse(response.body) }
|
18
|
+
|
19
|
+
it 'does not contain errors' do
|
20
|
+
expect(subject).not_to(have_key('errors'))
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'contains the current application revision' do
|
24
|
+
expect(subject.fetch('revision')).to(match(/^[a-f0-9]{40}$/))
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'contains the current time in ISO8601' do
|
28
|
+
time = Time.now
|
29
|
+
Time.stub(:now).and_return(time)
|
30
|
+
expect(subject.fetch('time')).to(match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/))
|
31
|
+
expect(subject.fetch('time')).to(eq(time.utc.iso8601))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
shared_examples_for 'an erred status response' do
|
37
|
+
it 'responds with HTTP 500' do
|
38
|
+
expect(subject.response_code).to(eq(500))
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'responds with a JSON content header' do
|
42
|
+
expect(subject.content_type).to(eq(Mime::JSON))
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'responds with valid JSON' do
|
46
|
+
expect { JSON.parse(subject.body) }.not_to(raise_error)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'contains errors' do
|
50
|
+
expect(JSON.parse(subject.body)).to(have_key('errors'))
|
51
|
+
expect(JSON.parse(subject.body).fetch('errors')).not_to(be_empty)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
RSpec::Matchers.define :include_status_error_message do |message|
|
56
|
+
match do |response|
|
57
|
+
@body = JSON.parse(response.body)
|
58
|
+
@body.fetch('errors', {}).fetch('base').include?(message)
|
59
|
+
end
|
60
|
+
|
61
|
+
failure_message_for_should do |actual|
|
62
|
+
"expected #{@body.inspect} to include a #{message.inspect} error message"
|
63
|
+
end
|
64
|
+
|
65
|
+
failure_message_for_should_not do |actual|
|
66
|
+
"expected #{@body.inspect} to not include a #{message.inspect} error message"
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rapporteur
|
2
|
+
# An ActiveModel::Serializer used to serialize the checker data for JSON
|
3
|
+
# rendering.
|
4
|
+
#
|
5
|
+
class Serializer < ActiveModel::Serializer
|
6
|
+
self.root = false
|
7
|
+
|
8
|
+
attributes :revision,
|
9
|
+
:time
|
10
|
+
|
11
|
+
# Internal: Converts the checker instance time into UTC to provide a
|
12
|
+
# consistent public representation.
|
13
|
+
#
|
14
|
+
# Returns a Time instance in UTC.
|
15
|
+
#
|
16
|
+
def time
|
17
|
+
object.time.utc
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/rapporteur.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'active_model_serializers'
|
2
|
+
|
3
|
+
require "rapporteur/engine" if defined?(Rails)
|
4
|
+
require "rapporteur/version"
|
5
|
+
|
6
|
+
# Rapporteur is a Rails Engine which provides your application with an
|
7
|
+
# application status endpoint.
|
8
|
+
#
|
9
|
+
module Rapporteur
|
10
|
+
autoload :Checker, 'rapporteur/checker'
|
11
|
+
autoload :Checks, 'rapporteur/checks'
|
12
|
+
autoload :Responder, 'rapporteur/responder'
|
13
|
+
autoload :Revision, 'rapporteur/revision'
|
14
|
+
autoload :Serializer, 'rapporteur/serializer'
|
15
|
+
end
|
data/rapporteur.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rapporteur/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rapporteur"
|
8
|
+
spec.version = Rapporteur::VERSION
|
9
|
+
spec.authors = ["Envy Labs", "Code School"]
|
10
|
+
spec.email = [""]
|
11
|
+
spec.description = %q{An engine that provides common status polling endpoint.}
|
12
|
+
spec.summary = %q{An engine that provides common status polling endpoint.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'active_model_serializers', '>= 0.8'
|
22
|
+
spec.add_dependency 'railties', '~> 3.0'
|
23
|
+
|
24
|
+
spec.add_development_dependency "appraisal", "~> 0.5"
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "combustion", "~> 0.5"
|
27
|
+
spec.add_development_dependency "rails", "~> 3.0"
|
28
|
+
spec.add_development_dependency "rake"
|
29
|
+
spec.add_development_dependency "rspec-rails", "~> 2.0"
|
30
|
+
spec.add_development_dependency "sqlite3"
|
31
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
*.log
|
File without changes
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'A status request with an ActiveRecordCheck' do
|
4
|
+
before do
|
5
|
+
Rapporteur::Checker.add_check(Rapporteur::Checks::ActiveRecordCheck)
|
6
|
+
end
|
7
|
+
|
8
|
+
subject { get(status_path) ; response }
|
9
|
+
|
10
|
+
context 'with an unerring ActiveRecord connection' do
|
11
|
+
it_behaves_like 'a successful status response'
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'with a failed ActiveRecord connection' do
|
15
|
+
before do
|
16
|
+
ActiveRecord::Base.connection.stub(:execute).
|
17
|
+
and_raise(ActiveRecord::ConnectionNotEstablished)
|
18
|
+
end
|
19
|
+
|
20
|
+
it_behaves_like 'an erred status response'
|
21
|
+
|
22
|
+
it 'contains a message regarding the database failure' do
|
23
|
+
expect(subject).to include_status_error_message(I18n.t('activemodel.errors.models.rapporteur/checker.attributes.base.database_unavailable'))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe "status routes" do
|
4
|
+
it 'routes /status.json to statuses#show' do
|
5
|
+
expect({ get: '/status.json'}).to route_to({
|
6
|
+
action: 'show',
|
7
|
+
controller: 'statuses',
|
8
|
+
format: 'json',
|
9
|
+
})
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'does not route /status' do
|
13
|
+
expect({ get: '/status'}).to_not be_routable
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'does not route /status.html' do
|
17
|
+
expect({ get: '/status.html'}).to_not be_routable
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'does not route /status.xml' do
|
21
|
+
expect({ get: '/status.xml'}).to_not be_routable
|
22
|
+
end
|
23
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'combustion'
|
4
|
+
|
5
|
+
ENV["RAILS_ENV"] ||= 'test'
|
6
|
+
Combustion.initialize! :action_controller, :active_record
|
7
|
+
|
8
|
+
require 'rspec/rails'
|
9
|
+
require 'rapporteur/rspec'
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
13
|
+
config.run_all_when_everything_filtered = true
|
14
|
+
config.filter_run :focus
|
15
|
+
|
16
|
+
config.order = 'random'
|
17
|
+
|
18
|
+
config.before { Rapporteur::Checker.clear }
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,215 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rapporteur
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Envy Labs
|
8
|
+
- Code School
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-19 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: active_model_serializers
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ! '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0.8'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ! '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0.8'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: railties
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '3.0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '3.0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: appraisal
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ~>
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0.5'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0.5'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: bundler
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '1.3'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.3'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: combustion
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0.5'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ~>
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0.5'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rails
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ~>
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '3.0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ~>
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '3.0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: rake
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ! '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: rspec-rails
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ~>
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '2.0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '2.0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: sqlite3
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ! '>='
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
type: :development
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ! '>='
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
description: An engine that provides common status polling endpoint.
|
141
|
+
email:
|
142
|
+
- ''
|
143
|
+
executables: []
|
144
|
+
extensions: []
|
145
|
+
extra_rdoc_files: []
|
146
|
+
files:
|
147
|
+
- .gitignore
|
148
|
+
- .rspec
|
149
|
+
- .travis.yml
|
150
|
+
- Appraisals
|
151
|
+
- CHANGELOG.md
|
152
|
+
- Gemfile
|
153
|
+
- LICENSE.txt
|
154
|
+
- README.md
|
155
|
+
- Rakefile
|
156
|
+
- app/controllers/statuses_controller.rb
|
157
|
+
- config.ru
|
158
|
+
- config/locales/en.yml
|
159
|
+
- config/routes.rb
|
160
|
+
- gemfiles/rails3.1.gemfile
|
161
|
+
- gemfiles/rails3.2.gemfile
|
162
|
+
- lib/rapporteur.rb
|
163
|
+
- lib/rapporteur/checker.rb
|
164
|
+
- lib/rapporteur/checks.rb
|
165
|
+
- lib/rapporteur/checks/active_record_check.rb
|
166
|
+
- lib/rapporteur/engine.rb
|
167
|
+
- lib/rapporteur/responder.rb
|
168
|
+
- lib/rapporteur/revision.rb
|
169
|
+
- lib/rapporteur/rspec.rb
|
170
|
+
- lib/rapporteur/serializer.rb
|
171
|
+
- lib/rapporteur/version.rb
|
172
|
+
- rapporteur.gemspec
|
173
|
+
- spec/internal/config/database.yml
|
174
|
+
- spec/internal/config/routes.rb
|
175
|
+
- spec/internal/db/schema.rb
|
176
|
+
- spec/internal/log/.gitignore
|
177
|
+
- spec/internal/public/favicon.ico
|
178
|
+
- spec/requests/active_record_check_spec.rb
|
179
|
+
- spec/requests/no_checks_spec.rb
|
180
|
+
- spec/routing/routes_spec.rb
|
181
|
+
- spec/spec_helper.rb
|
182
|
+
homepage: ''
|
183
|
+
licenses:
|
184
|
+
- MIT
|
185
|
+
metadata: {}
|
186
|
+
post_install_message:
|
187
|
+
rdoc_options: []
|
188
|
+
require_paths:
|
189
|
+
- lib
|
190
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ! '>='
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
196
|
+
requirements:
|
197
|
+
- - ! '>='
|
198
|
+
- !ruby/object:Gem::Version
|
199
|
+
version: '0'
|
200
|
+
requirements: []
|
201
|
+
rubyforge_project:
|
202
|
+
rubygems_version: 2.0.3
|
203
|
+
signing_key:
|
204
|
+
specification_version: 4
|
205
|
+
summary: An engine that provides common status polling endpoint.
|
206
|
+
test_files:
|
207
|
+
- spec/internal/config/database.yml
|
208
|
+
- spec/internal/config/routes.rb
|
209
|
+
- spec/internal/db/schema.rb
|
210
|
+
- spec/internal/log/.gitignore
|
211
|
+
- spec/internal/public/favicon.ico
|
212
|
+
- spec/requests/active_record_check_spec.rb
|
213
|
+
- spec/requests/no_checks_spec.rb
|
214
|
+
- spec/routing/routes_spec.rb
|
215
|
+
- spec/spec_helper.rb
|