easymon 1.1 → 1.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of easymon might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +19 -9
- data/app/controllers/easymon/checks_controller.rb +41 -14
- data/lib/easymon/checklist.rb +17 -17
- data/lib/easymon/repository.rb +12 -22
- data/lib/easymon/result.rb +14 -8
- data/lib/easymon/version.rb +1 -1
- data/test/controllers/easymon/checks_controller_test.rb +24 -15
- data/test/dummy/config/initializers/easymon.rb +1 -1
- data/test/dummy/log/development.log +938 -0
- data/test/dummy/log/test.log +6331 -0
- data/test/dummy/tmp/cache/4D4/7A0/health_check +1 -1
- data/test/unit/checklist_test.rb +19 -19
- data/test/unit/repository_test.rb +18 -24
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f475318f54ddf8f1b17ce97713976eb5db32c694
|
4
|
+
data.tar.gz: 37c240008c8ad2ac324ea3a1c1d3cd43e51b0c47
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c0e9ed818a719eae7db9bea397a49f221a3400a26b14822809cde36e19d5b5abb623854725325041530de0897f836541dbd67f8dfb72afd1c7f0ad2ebbbdc7b
|
7
|
+
data.tar.gz: a9fa4f0811bdd8dd2ca2dbc1c8338aa617bc462503661fe6b4148243ea25766d8a20cfdf561e24ecc4dfd5e54111323912415599137a899a8c92e916bbc99993
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ application database, a memcached connection, or a redis instance. These test
|
|
6
6
|
results can be used by a load balancer to determine the general health and
|
7
7
|
viability of the node your application is running on.
|
8
8
|
|
9
|
-
It's packaged up as a rails engine for 3.1 and greater, and a plugin for 2.3 -
|
9
|
+
It's packaged up as a rails engine for 3.1 and greater, and a plugin for 2.3 -
|
10
10
|
3.0.
|
11
11
|
|
12
12
|
## History
|
@@ -23,7 +23,7 @@ gem 'easymon'
|
|
23
23
|
````
|
24
24
|
|
25
25
|
## Usage
|
26
|
-
To get started, you'll need to add an initializer for this to do anything.
|
26
|
+
To get started, you'll need to add an initializer for this to do anything.
|
27
27
|
In `config/initializers/easymon.rb`:
|
28
28
|
|
29
29
|
````ruby
|
@@ -87,7 +87,16 @@ individually available at:
|
|
87
87
|
* `/up/memcached`
|
88
88
|
* `/up/critical` - Runs both the application-database and redis checks.
|
89
89
|
|
90
|
-
##
|
90
|
+
## Checks
|
91
|
+
|
92
|
+
A check can be any ruby code that responds_to? a #check method that returns a
|
93
|
+
two element array. The first element is the result of executing the check and
|
94
|
+
should be true or false. The second element is the message describing what's
|
95
|
+
going on. The array would look something like this: `[true, "Up"]` in the
|
96
|
+
case of a successful check or `[false, "Timeout"]` in the case of a failed
|
97
|
+
check.
|
98
|
+
|
99
|
+
### Included Checks
|
91
100
|
|
92
101
|
* ActiveRecord
|
93
102
|
* Redis
|
@@ -123,11 +132,11 @@ Easymon::RedisCheck.new(
|
|
123
132
|
This is the most visually complex test to instantiate, but it's only because
|
124
133
|
we're loading the config from disk and getting the config block that matches
|
125
134
|
the Rails.env in one line. As long as you pass a hash that can be used by
|
126
|
-
Redis.new.
|
135
|
+
Redis.new, it doesn't care where the config comes from.
|
127
136
|
|
128
137
|
### Memcached
|
129
138
|
`Easymon::MemcachedCheck` is a basic check that will write and then read a key
|
130
|
-
from the cache. It expects a cache instance to check, so it could be as easy
|
139
|
+
from the cache. It expects a cache instance to check, so it could be as easy
|
131
140
|
as:
|
132
141
|
|
133
142
|
````ruby
|
@@ -153,6 +162,7 @@ to the node.
|
|
153
162
|
````ruby
|
154
163
|
Easymon::TrafficEnabledCheck.new("enable-traffic")
|
155
164
|
````
|
165
|
+
This is a subclass of the Semaphore check mentioned above.
|
156
166
|
|
157
167
|
###Split ActiveRecord
|
158
168
|
`Easymon::SplitActiveRecordCheck` is the most complicated check, as it's not
|
@@ -190,9 +200,9 @@ Easymon::Repository.add("split-database", check)
|
|
190
200
|
````
|
191
201
|
|
192
202
|
### Http
|
193
|
-
`Easymon::HttpCheck` will check the return status of a HEAD request to a URL.
|
194
|
-
Great for checking service endpoint availability! The following will make a
|
195
|
-
request to port 9200 on localhost, which is where you might have Elasticsearch
|
203
|
+
`Easymon::HttpCheck` will check the return status of a HEAD request to a URL.
|
204
|
+
Great for checking service endpoint availability! The following will make a
|
205
|
+
request to port 9200 on localhost, which is where you might have Elasticsearch
|
196
206
|
running:
|
197
207
|
|
198
208
|
````ruby
|
@@ -220,7 +230,7 @@ Here's the most direct way to get your work merged into the project:
|
|
220
230
|
8. Push the branch up
|
221
231
|
9. Send a pull request for your branch
|
222
232
|
|
223
|
-
If you're going to make a major change ask first to
|
233
|
+
If you're going to make a major change ask first to make sure it's in line with
|
224
234
|
the project goals.
|
225
235
|
|
226
236
|
## To Do
|
@@ -9,22 +9,23 @@ module Easymon
|
|
9
9
|
format.json { render :json => e.message, :status => :not_found }
|
10
10
|
end
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
def index
|
14
14
|
checklist = Easymon::Repository.all
|
15
15
|
checklist.check
|
16
|
-
|
16
|
+
|
17
17
|
message = "No Checks Defined"
|
18
|
-
|
18
|
+
|
19
19
|
response_status = checklist.response_status
|
20
20
|
message = checklist.to_s unless checklist.empty?
|
21
|
-
|
21
|
+
|
22
22
|
unless checklist.empty?
|
23
23
|
# override response_status if we have a "critical" checklist
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
unless Easymon::Repository.critical.empty?
|
25
|
+
critical_checks = checklist.items.map{|name, entry| checklist.results[name] if Easymon::Repository.critical.include?(name)}.compact
|
26
|
+
critical_success = critical_checks.all?(&:success?)
|
27
|
+
response_status = critical_success ? :ok : :service_unavailable
|
28
|
+
message = add_prefix(critical_success, message)
|
28
29
|
else
|
29
30
|
message = add_prefix(checklist.success?, message)
|
30
31
|
end
|
@@ -37,14 +38,40 @@ module Easymon
|
|
37
38
|
end
|
38
39
|
|
39
40
|
def show
|
40
|
-
check = Easymon::Repository.fetch(params[:check])
|
41
41
|
check_result = []
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
is_critical = params[:check] == "critical"
|
43
|
+
|
44
|
+
if is_critical
|
45
|
+
# Build the critical checklist
|
46
|
+
checklist_proto = {}
|
47
|
+
Easymon::Repository.critical.each do |name|
|
48
|
+
checklist_proto[name] = Easymon::Repository.fetch(name)
|
49
|
+
end
|
50
|
+
checklist = Easymon::Checklist.new checklist_proto
|
51
|
+
checklist.check
|
52
|
+
else
|
53
|
+
check = Easymon::Repository.fetch(params[:check])
|
54
|
+
timing = Benchmark.realtime { check_result = check[:check].check }
|
55
|
+
result = Easymon::Result.new(check_result, timing, check[:critical])
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
|
45
60
|
respond_to do |format|
|
46
|
-
|
47
|
-
|
61
|
+
format.any(:text, :html) do
|
62
|
+
if is_critical
|
63
|
+
render :text => add_prefix(checklist.success?, checklist), :status => checklist.response_status
|
64
|
+
else
|
65
|
+
render :text => result, :status => result.response_status
|
66
|
+
end
|
67
|
+
end
|
68
|
+
format.json do
|
69
|
+
if is_critical
|
70
|
+
render :json => checklist, :status => checklist.response_status
|
71
|
+
else
|
72
|
+
render :json => result, :status => result.response_status
|
73
|
+
end
|
74
|
+
end
|
48
75
|
end
|
49
76
|
end
|
50
77
|
|
data/lib/easymon/checklist.rb
CHANGED
@@ -4,63 +4,63 @@ module Easymon
|
|
4
4
|
class Checklist
|
5
5
|
extend Forwardable
|
6
6
|
def_delegators :@items, :size, :include?, :empty?
|
7
|
-
|
7
|
+
|
8
8
|
attr_accessor :items
|
9
9
|
attr_accessor :results
|
10
|
-
|
10
|
+
|
11
11
|
def initialize(items={})
|
12
12
|
self.items = items
|
13
13
|
self.results = {}
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def check
|
17
17
|
self.results = items.inject({}) do |hash, (name, check)|
|
18
18
|
check_result = []
|
19
|
-
timing = Benchmark.realtime { check_result = check.check }
|
20
|
-
hash[name] = Easymon::Result.new(check_result, timing)
|
19
|
+
timing = Benchmark.realtime { check_result = check[:check].check }
|
20
|
+
hash[name] = Easymon::Result.new(check_result, timing, check[:critical])
|
21
21
|
hash
|
22
22
|
end
|
23
23
|
[self.success?, self.to_s]
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def timing
|
27
27
|
results.values.map{|r| r.timing}.inject(0, :+)
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
def to_text
|
31
31
|
to_s
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
def to_s
|
35
|
-
results.map{|name, result| "#{name}: #{result.to_s}"}.join("\n") +
|
35
|
+
results.map{|name, result| "#{name}: #{result.to_s}"}.join("\n") +
|
36
36
|
"\n - Total Time - " + Easymon.timing_to_ms(self.timing) + "ms"
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def to_hash
|
40
|
-
combined = {}
|
40
|
+
combined = {:timing => Easymon.timing_to_ms(timing)}
|
41
41
|
results.each do |name, result|
|
42
42
|
combined[name] = result.to_hash
|
43
43
|
end
|
44
44
|
combined
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
def as_json(*args)
|
48
48
|
to_hash
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
51
|
def success?
|
52
52
|
return false if results.empty?
|
53
53
|
results.values.all?(&:success?)
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
def response_status
|
57
57
|
success? ? :ok : :service_unavailable
|
58
58
|
end
|
59
|
-
|
60
|
-
# The following method could be implemented as a def_delegator by
|
59
|
+
|
60
|
+
# The following method could be implemented as a def_delegator by
|
61
61
|
# extending Forwardable, but since we want to catch IndexError and
|
62
62
|
# raise Easymon::NoSuchCheck, we'll be explicit here.
|
63
|
-
#
|
63
|
+
#
|
64
64
|
def fetch(name)
|
65
65
|
items.fetch(name)
|
66
66
|
rescue IndexError
|
data/lib/easymon/repository.rb
CHANGED
@@ -1,46 +1,36 @@
|
|
1
1
|
module Easymon
|
2
2
|
class Repository
|
3
3
|
attr_reader :repository
|
4
|
-
|
5
|
-
|
4
|
+
|
6
5
|
def self.fetch(name)
|
7
|
-
|
8
|
-
return repository.fetch(name)
|
9
|
-
else
|
10
|
-
critical.fetch(name)
|
11
|
-
end
|
6
|
+
return repository.fetch(name)
|
12
7
|
rescue IndexError
|
13
8
|
raise NoSuchCheck, "No check named '#{name}'"
|
14
9
|
end
|
15
|
-
|
10
|
+
|
16
11
|
def self.all
|
17
12
|
Checklist.new repository
|
18
13
|
end
|
19
|
-
|
14
|
+
|
20
15
|
def self.names
|
21
|
-
repository.keys
|
16
|
+
repository.keys
|
22
17
|
end
|
23
|
-
|
18
|
+
|
24
19
|
def self.add(name, check, is_critical=false)
|
25
|
-
|
26
|
-
|
27
|
-
repository["critical"] = Checklist.new critical
|
28
|
-
else
|
29
|
-
repository[name] = check
|
30
|
-
end
|
20
|
+
entry = {:check => check, :critical => is_critical ? true : false}
|
21
|
+
repository[name] = entry
|
31
22
|
end
|
32
|
-
|
23
|
+
|
33
24
|
def self.remove(name)
|
34
25
|
repository.delete(name)
|
35
|
-
critical.delete(name)
|
36
26
|
end
|
37
|
-
|
27
|
+
|
38
28
|
def self.repository
|
39
29
|
@repository ||= {}
|
40
30
|
end
|
41
|
-
|
31
|
+
|
42
32
|
def self.critical
|
43
|
-
|
33
|
+
repository.map{ |name, entry| name if entry[:critical] }.compact
|
44
34
|
end
|
45
35
|
end
|
46
36
|
end
|
data/lib/easymon/result.rb
CHANGED
@@ -3,31 +3,37 @@ module Easymon
|
|
3
3
|
attr_accessor :success
|
4
4
|
attr_accessor :message
|
5
5
|
attr_accessor :timing
|
6
|
-
|
7
|
-
|
6
|
+
attr_accessor :critical
|
7
|
+
|
8
|
+
def initialize(result, timing, is_critical = false)
|
8
9
|
self.success = result[0]
|
9
10
|
self.message = result[1]
|
10
11
|
self.timing = timing
|
12
|
+
self.critical = is_critical
|
11
13
|
end
|
12
|
-
|
14
|
+
|
13
15
|
def success?
|
14
16
|
success
|
15
17
|
end
|
16
|
-
|
18
|
+
|
19
|
+
def is_critical?
|
20
|
+
critical
|
21
|
+
end
|
22
|
+
|
17
23
|
def response_status
|
18
24
|
success? ? :ok : :service_unavailable
|
19
25
|
end
|
20
|
-
|
26
|
+
|
21
27
|
def to_s
|
22
28
|
"#{message} - #{Easymon.timing_to_ms(timing)}ms"
|
23
29
|
end
|
24
|
-
|
30
|
+
|
25
31
|
def as_json(options = {})
|
26
32
|
to_hash
|
27
33
|
end
|
28
|
-
|
34
|
+
|
29
35
|
def to_hash
|
30
|
-
{:success => success, :message => message, :timing => Easymon.timing_to_ms(timing)}
|
36
|
+
{:success => success, :message => message, :timing => Easymon.timing_to_ms(timing), :critical => critical}
|
31
37
|
end
|
32
38
|
end
|
33
39
|
end
|
data/lib/easymon/version.rb
CHANGED
@@ -2,13 +2,13 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
module Easymon
|
4
4
|
class ChecksControllerTest < ActionController::TestCase
|
5
|
-
|
5
|
+
|
6
6
|
test "index when no checks are defined" do
|
7
7
|
get :index, :use_route => :easymon
|
8
8
|
assert_response :service_unavailable
|
9
9
|
assert_equal "No Checks Defined", response.body
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
test "index when all checks pass" do
|
13
13
|
Easymon::Repository.add("database", Easymon::ActiveRecordCheck.new(ActiveRecord::Base))
|
14
14
|
get :index, :use_route => :easymon
|
@@ -16,6 +16,16 @@ module Easymon
|
|
16
16
|
assert response.body.include?("OK"), "Should include 'OK' in response body"
|
17
17
|
end
|
18
18
|
|
19
|
+
test "index when a check fails" do
|
20
|
+
Easymon::Repository.add("database", Easymon::ActiveRecordCheck.new(ActiveRecord::Base))
|
21
|
+
Easymon::Repository.add("redis", Easymon::RedisCheck.new(YAML.load_file(Rails.root.join("config/redis.yml"))[Rails.env].symbolize_keys))
|
22
|
+
Redis.any_instance.stubs(:ping).raises("boom")
|
23
|
+
get :index, use_route: :easymon
|
24
|
+
assert_response :service_unavailable
|
25
|
+
assert response.body.include?("redis: Down"), "Should include failure text, got #{response.body}"
|
26
|
+
assert response.body.include?("DOWN"), "Should include 'OK' in response body"
|
27
|
+
end
|
28
|
+
|
19
29
|
test "index when a critical check fails" do
|
20
30
|
Easymon::Repository.add("database", Easymon::ActiveRecordCheck.new(ActiveRecord::Base), :critical)
|
21
31
|
ActiveRecord::Base.connection.stubs(:select_value).raises("boom")
|
@@ -23,7 +33,7 @@ module Easymon
|
|
23
33
|
assert_response :service_unavailable
|
24
34
|
assert response.body.include?("database: Down"), "Should include failure text, got #{response.body}"
|
25
35
|
end
|
26
|
-
|
36
|
+
|
27
37
|
test "index when a non-critical check fails" do
|
28
38
|
Easymon::Repository.add("database", Easymon::ActiveRecordCheck.new(ActiveRecord::Base), :critical)
|
29
39
|
Easymon::Repository.add("redis", Easymon::RedisCheck.new(YAML.load_file(Rails.root.join("config/redis.yml"))[Rails.env].symbolize_keys))
|
@@ -33,48 +43,47 @@ module Easymon
|
|
33
43
|
assert response.body.include?("redis: Down"), "Should include failure text, got #{response.body}"
|
34
44
|
assert response.body.include?("OK"), "Should include 'OK' in response body"
|
35
45
|
end
|
36
|
-
|
46
|
+
|
37
47
|
test "index returns valid json" do
|
38
48
|
Easymon::Repository.add("database", Easymon::ActiveRecordCheck.new(ActiveRecord::Base))
|
39
49
|
get :index, :use_route => :easymon, :format => :json
|
40
|
-
|
50
|
+
|
41
51
|
json = JSON.parse(response.body)
|
42
52
|
|
43
53
|
assert json.has_key?("database")
|
44
54
|
assert_equal "Up", json["database"]["message"]
|
45
|
-
assert_equal 1, json.keys.count
|
46
55
|
end
|
47
|
-
|
56
|
+
|
48
57
|
test "show when the check passes" do
|
49
58
|
Easymon::Repository.add("database", Easymon::ActiveRecordCheck.new(ActiveRecord::Base))
|
50
59
|
get :show, :use_route => :easymon, check: "database"
|
51
60
|
assert_response :success
|
52
61
|
assert response.body.include?("Up"), "Response should include message text, got #{response.body}"
|
53
62
|
end
|
54
|
-
|
63
|
+
|
55
64
|
test "show json when the check passes" do
|
56
65
|
Easymon::Repository.add("database", Easymon::ActiveRecordCheck.new(ActiveRecord::Base))
|
57
66
|
get :show, :use_route => :easymon, :check => "database", :format => :json
|
58
|
-
|
67
|
+
|
59
68
|
json = JSON.parse(response.body)
|
60
|
-
|
69
|
+
|
61
70
|
assert json.has_key?("message")
|
62
71
|
assert_equal "Up", json["message"]
|
63
72
|
end
|
64
|
-
|
73
|
+
|
65
74
|
test "show when the check fails" do
|
66
75
|
Easymon::Repository.add("database", Easymon::ActiveRecordCheck.new(ActiveRecord::Base))
|
67
76
|
ActiveRecord::Base.connection.stubs(:select_value).raises("boom")
|
68
|
-
|
77
|
+
|
69
78
|
get :show, :use_route => :easymon, :check => "database"
|
70
|
-
|
79
|
+
|
71
80
|
assert_response :service_unavailable
|
72
81
|
assert response.body.include?("Down"), "Response should include failure text, got #{response.body}"
|
73
82
|
end
|
74
|
-
|
83
|
+
|
75
84
|
test "show if the check is not found" do
|
76
85
|
Easymon::Repository.names.each {|name| Easymon::Repository.remove(name)}
|
77
|
-
|
86
|
+
|
78
87
|
get :show, :use_route => :easymon, :check => "database"
|
79
88
|
assert_response :not_found
|
80
89
|
end
|