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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2f61e81b90d2bee2ab041290388bdc30971b544d
4
- data.tar.gz: 7d1cc5cb52e9210533cd4f4dce9786c710e76405
3
+ metadata.gz: f475318f54ddf8f1b17ce97713976eb5db32c694
4
+ data.tar.gz: 37c240008c8ad2ac324ea3a1c1d3cd43e51b0c47
5
5
  SHA512:
6
- metadata.gz: 0104d61a2ae1fdbecc7b2178caf0eb610eb32b3a4c2d0bc5d413bb6abe75840a1e73b8b285e42a8a48291061e92783f6053d5a29f5e3280aca7ed3e9102ee7be
7
- data.tar.gz: e4e71ccaac26b483bdbca036c942de0cf55103341ecbe4947bf57ca5ba3c13656366fa725aeb75fae30513aab2673650e2fc968c710cfb11d46a2b4670ec8019
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
- ## Included Checks
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 maje sure it's in line with
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
- if checklist.include?("critical")
25
- critical_checklist = checklist.fetch("critical")
26
- response_status = critical_checklist.response_status
27
- message = add_prefix(critical_checklist.success?, message)
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
- timing = Benchmark.realtime { check_result = check.check }
43
- result = Easymon::Result.new(check_result, timing)
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
- format.any(:text, :html) { render :text => result, :status => result.response_status }
47
- format.json { render :json => result, :status => result.response_status }
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
 
@@ -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
@@ -1,46 +1,36 @@
1
1
  module Easymon
2
2
  class Repository
3
3
  attr_reader :repository
4
- attr_reader :critical
5
-
4
+
6
5
  def self.fetch(name)
7
- if repository.include?(name)
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 + critical.keys
16
+ repository.keys
22
17
  end
23
-
18
+
24
19
  def self.add(name, check, is_critical=false)
25
- if is_critical
26
- critical[name] = check
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
- @critical ||= {}
33
+ repository.map{ |name, entry| name if entry[:critical] }.compact
44
34
  end
45
35
  end
46
36
  end
@@ -3,31 +3,37 @@ module Easymon
3
3
  attr_accessor :success
4
4
  attr_accessor :message
5
5
  attr_accessor :timing
6
-
7
- def initialize(result, timing)
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
@@ -1,3 +1,3 @@
1
1
  module Easymon
2
- VERSION = "1.1"
2
+ VERSION = "1.2"
3
3
  end
@@ -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