abril_heartbeat 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.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +122 -0
  8. data/Rakefile +6 -0
  9. data/abril_heartbeat.gemspec +32 -0
  10. data/lib/abril_heartbeat.rb +46 -0
  11. data/lib/abril_heartbeat/checkers/abstract_checker.rb +22 -0
  12. data/lib/abril_heartbeat/checkers/mongo_checker.rb +20 -0
  13. data/lib/abril_heartbeat/checkers/mysql_checker.rb +20 -0
  14. data/lib/abril_heartbeat/checkers/redis_checker.rb +20 -0
  15. data/lib/abril_heartbeat/checkers/rest_checker.rb +38 -0
  16. data/lib/abril_heartbeat/config_loader.rb +22 -0
  17. data/lib/abril_heartbeat/heartbeater.rb +23 -0
  18. data/lib/abril_heartbeat/version.rb +3 -0
  19. data/lib/abril_heartbeat/wrappers/mongo_wrapper.rb +11 -0
  20. data/lib/abril_heartbeat/wrappers/mysql_wrapper.rb +11 -0
  21. data/lib/abril_heartbeat/wrappers/redis_wrapper.rb +11 -0
  22. data/spec/cassettes/AbrilHeartbeat_Middleware/when_middleware_handles_the_request/responds_success.yml +111 -0
  23. data/spec/cassettes/HeartbeatAbril_Middleware/when_middleware_handles_the_request/responds_success.yml +111 -0
  24. data/spec/checkers/abstract_checker_spec.rb +5 -0
  25. data/spec/checkers/mongo_checker_spec.rb +42 -0
  26. data/spec/checkers/mysql_checker_spec.rb +42 -0
  27. data/spec/checkers/redis_checker_spec.rb +42 -0
  28. data/spec/checkers/rest_checker_spec.rb +35 -0
  29. data/spec/factories/rest_status.rb +31 -0
  30. data/spec/heartbeater_spec.rb +53 -0
  31. data/spec/middleware/abril_heartbeat_spec.rb +38 -0
  32. data/spec/spec_helper.rb +43 -0
  33. data/spec/support/heartbeat_example.yml +11 -0
  34. data/spec/support/shared_examples/checker_share.rb +21 -0
  35. data/spec/support/vcr.rb +7 -0
  36. metadata +234 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e46a565059cc447a70e723f46152dfd92da1593a
4
+ data.tar.gz: 93eb57ddb1256eb7b8e5e10d9f9f757b675075cc
5
+ SHA512:
6
+ metadata.gz: e17eb02e8102b3b2412c2fd7b6affd21c2691c745160979e8839409027eb223d3a331a09c7ec340b74db380e14e2e90383e768b45518beb7c5b8ac1779951032
7
+ data.tar.gz: cd842688fcfcf30515e44808779c489901f88fa910d572b53f4bb6248fafa91a2621ea34ddfc096c2620eb0420a8c1325fe43e9fdc57c597228cb25b361286b9
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1 @@
1
+ ruby-2.1.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in abril_heartbeat.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Diogo Scudelletti
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.
@@ -0,0 +1,122 @@
1
+ # Abril Heartbeat
2
+
3
+ This GEM is a middleware which adds a heartbeat route to your Apps, a route which checks your external dependencies such as MySQL, Mongo, Redis and REST APIs.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'abril_heartbeat'
11
+ ```
12
+
13
+ Set the middleware on your app.
14
+
15
+ Rails Example
16
+ In the config/application.rb file add the following middleware.
17
+
18
+ ```ruby
19
+ config.middleware.use "AbrilHeartbeat::Middleware"
20
+ ```
21
+
22
+ Then access the `/heartbeat` in your app.
23
+
24
+ Response's example
25
+ ```json
26
+ [{
27
+ MONGO: {
28
+ status: "FAIL",
29
+ status_message: "Could not connect to any secondary or primary nodes for replica set <Moped::Cluster nodes=[<Moped::Node resolved_address="
30
+ 127.0.0.1: 27017 ">]>"
31
+ }
32
+ }]
33
+ ```
34
+
35
+ ### REST Configuration
36
+
37
+ Create a heartbeat.yml to your REST APIs.
38
+
39
+ heartbeat.yml example:
40
+ ```yaml
41
+ api_success:
42
+ url: 'http://some.awesomeapi.com'
43
+ type: 'rest'
44
+
45
+ api_not_found:
46
+ url: 'http://another.api.com'
47
+ type: 'rest'
48
+ ```
49
+
50
+ For APIs checking, create a heartbeat.yml and pass it in the middleware initialization:
51
+ ```ruby
52
+ config.middleware.use "AbrilHeartbeat::Middleware", :file_path => "#{File.dirname(__FILE__)}/heartbeat.yml"
53
+ ```
54
+
55
+ Response's example
56
+ ```json
57
+ [{
58
+ REST: [{
59
+ api_success: {
60
+ url: "http://some.awesomeapi.com",
61
+ status: 200,
62
+ status_message: "OK"
63
+ }
64
+ }, {
65
+ api_not_found: {
66
+ url: "http://another.api.com",
67
+ status: 404,
68
+ status_message: "Page Not Found"
69
+ }
70
+ }, {
71
+ api_wrong_url: {
72
+ url: "I am a wrong url",
73
+ status: null,
74
+ status_message: "bad URI(is not URI?): http://I am a wrong url"
75
+ }
76
+ }]
77
+ }, {
78
+ MONGO: {
79
+ status: "FAIL",
80
+ status_message: "Could not connect to any secondary or primary nodes for replica set <Moped::Cluster nodes=[<Moped::Node resolved_address="
81
+ 127.0.0.1: 27017 ">]>"
82
+ }
83
+ }]
84
+ ```
85
+
86
+ ### Redis Configuration
87
+
88
+ By Default we access a REDIS client variable, which contains the redis client.
89
+
90
+ ### Mongo Configuration
91
+
92
+ By default we use the Mongoid class of your app to check the connection.
93
+
94
+ ### ActiveRecord Configuration
95
+
96
+ By default we use the ActiveRecord to check the connection.
97
+
98
+ ## Creating your own checkers.
99
+
100
+ In the middleware initialization you can pass your own checkers:
101
+
102
+ ```ruby
103
+ config.middleware.use "AbrilHeartbeat::Middleware", {custom_checkers: [YourCustomCheckerClass]}
104
+ ```
105
+
106
+ You custom checker class must implement the `AbrilHeartbeat::AbstractChecker` interface.
107
+
108
+ ## Future
109
+
110
+ * add new MongoDrivers to support more apps
111
+ * use the yaml file to get the Redis client instance
112
+ * basic auth? At this moment you must handle the request before our middleware
113
+ * add a initializer to config the heartbeat route
114
+
115
+
116
+ ## Contributing
117
+
118
+ 1. Fork it ( https://github.com/abril/abril_heartbeat/fork )
119
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
120
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
121
+ 4. Push to the branch (`git push origin my-new-feature`)
122
+ 5. Create a new Pull Request
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require "rspec/core/rake_task"
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'abril_heartbeat/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "abril_heartbeat"
8
+ spec.version = AbrilHeartbeat::VERSION
9
+ spec.authors = ["Diogo Scudelletti"]
10
+ spec.email = ["diogo@scudelletti.com"]
11
+ spec.summary = %q{Builds a heartbeat route for Apps}
12
+ spec.description = %q{This GEM is a middleware which adds a heartbeat route to your Apps, a route which checks your external dependencies such as MySQL, Mongo, Redis and REST APIs.}
13
+ spec.homepage = "https://github.com/abril/abril_heartbeat"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
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 "rack", "~> 1.4.5"
22
+ spec.add_dependency "rest-client", "~> 1.7.2"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.1.0"
27
+ spec.add_development_dependency "rack-test", "~> 0.6.2"
28
+ spec.add_development_dependency "factory_girl", "~> 4.4.0"
29
+ spec.add_development_dependency "vcr", "~> 2.9.3"
30
+ spec.add_development_dependency "webmock", "~> 1.18.0"
31
+ spec.add_development_dependency "pry-nav", "~> 0.2.4"
32
+ end
@@ -0,0 +1,46 @@
1
+ require "json"
2
+ require "abril_heartbeat/version"
3
+ require "abril_heartbeat/heartbeater"
4
+
5
+ # Config Loader
6
+ require "abril_heartbeat/config_loader"
7
+
8
+ # Checkers
9
+ require "abril_heartbeat/checkers/abstract_checker"
10
+ require "abril_heartbeat/checkers/rest_checker"
11
+ require "abril_heartbeat/checkers/mongo_checker"
12
+ require "abril_heartbeat/checkers/mysql_checker"
13
+ require "abril_heartbeat/checkers/redis_checker"
14
+
15
+ # Wrappers
16
+ require "abril_heartbeat/wrappers/mongo_wrapper"
17
+ require "abril_heartbeat/wrappers/mysql_wrapper"
18
+ require "abril_heartbeat/wrappers/redis_wrapper"
19
+
20
+ module AbrilHeartbeat
21
+ class Middleware
22
+ def initialize(app, options={})
23
+ @app = app
24
+ @file_path = options[:file_path]
25
+ end
26
+
27
+ def call(env)
28
+ @env = env
29
+ if request.path == '/heartbeat'
30
+ [200, { 'Content-Type' => 'text/json' }, [response.to_json]]
31
+ else
32
+ @app.call(env)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def response
39
+ Heartbeater.new({:file_path => @file_path}).run!
40
+ end
41
+
42
+ def request
43
+ Rack::Request.new @env
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,22 @@
1
+ module AbrilHeartbeat
2
+ class AbstractChecker
3
+ def self.is_running?
4
+ raise NotImplementedError
5
+ end
6
+
7
+ def self.module_name
8
+ raise NotImplementedError
9
+ end
10
+
11
+ def self.run!
12
+ status, message = check!
13
+ { module_name => { "status" => status, "status_message" => message }}
14
+ end
15
+
16
+ private
17
+
18
+ def self.check!
19
+ raise NotImplementedError
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module AbrilHeartbeat
2
+ class MongoChecker < AbstractChecker
3
+ def self.is_running?
4
+ MongoWrapper.has_client?
5
+ end
6
+
7
+ def self.module_name
8
+ "MONGO"
9
+ end
10
+
11
+ private
12
+
13
+ def self.check!
14
+ MongoWrapper.check_status!
15
+ ["OK", "Everything is under control"]
16
+ rescue => exception
17
+ ["FAIL", exception.message]
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module AbrilHeartbeat
2
+ class MysqlChecker < AbstractChecker
3
+ def self.is_running?
4
+ MysqlWrapper.has_client?
5
+ end
6
+
7
+ def self.module_name
8
+ "MYSQL"
9
+ end
10
+
11
+ private
12
+
13
+ def self.check!
14
+ MysqlWrapper.check_status!
15
+ ["OK", "Everything is under control"]
16
+ rescue => exception
17
+ ["FAIL", exception.message]
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module AbrilHeartbeat
2
+ class RedisChecker < AbstractChecker
3
+ def self.is_running?
4
+ RedisWrapper.has_client?
5
+ end
6
+
7
+ def self.module_name
8
+ "REDIS"
9
+ end
10
+
11
+ private
12
+
13
+ def self.check!
14
+ RedisWrapper.check_status!
15
+ ["OK", "Everything is under control"]
16
+ rescue => exception
17
+ ["FAIL", exception.message]
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,38 @@
1
+ require "rest_client"
2
+
3
+ module AbrilHeartbeat
4
+ class RestChecker < AbstractChecker
5
+ def self.is_running?
6
+ !rest_hash.empty?
7
+ end
8
+
9
+ def self.run!
10
+ messages = rest_hash.map do |key, value|
11
+ status, message = check!{ value['url'] }
12
+ { key => { "url" => value["url"], "status" => status, "status_message" => message }}
13
+ end
14
+
15
+ { module_name => messages }
16
+ end
17
+
18
+ def self.module_name
19
+ "REST"
20
+ end
21
+
22
+ private
23
+
24
+ def self.rest_hash
25
+ ConfigLoader.load_by_type(:rest)
26
+ end
27
+
28
+ def self.check!(&block)
29
+ url = yield
30
+ response = RestClient.get(url)
31
+ [response.code, "OK"]
32
+ rescue RestClient::ResourceNotFound
33
+ [404, "Page Not Found"]
34
+ rescue => exception
35
+ [nil, exception.message]
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ module AbrilHeartbeat
2
+ class ConfigLoader
3
+ def self.load
4
+ @file ||= load_file
5
+ end
6
+
7
+ def self.load_by_type(type)
8
+ return load if load.empty?
9
+ load.select{|_, v| v['type'] == type.to_s}
10
+ end
11
+
12
+ def self.set_file(file_path)
13
+ @file_path = file_path
14
+ end
15
+
16
+ def self.load_file
17
+ return {} unless @file_path
18
+
19
+ ::YAML.load_file(@file_path)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ require "yaml"
2
+
3
+ module AbrilHeartbeat
4
+ class Heartbeater
5
+ def initialize(options={})
6
+ ConfigLoader.set_file(options[:file_path]) if options[:file_path]
7
+
8
+ @checkers = [MongoChecker, MysqlChecker, RedisChecker]
9
+ @checkers += options[:custom_checkers] if options[:custom_checkers]
10
+ end
11
+
12
+ def run!
13
+ response = []
14
+ response << RestChecker.run! if RestChecker.is_running?
15
+
16
+ @checkers.each do |checker|
17
+ response << checker.run! if checker.is_running?
18
+ end
19
+
20
+ response
21
+ end
22
+ end
23
+ end