healthier 0.1.5 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b9085660c0bf98cddf325db77e9d7f7a655c34e4af17e0febcc2d0468d42a10
4
- data.tar.gz: b4cbbda7e758b1375c07d3ff98efa31ac85a52a9ede9c67dd2a06a95c75f9103
3
+ metadata.gz: 851390d021df1149267018e9ec714cee68168c2f1f9b02c0998790c33da63005
4
+ data.tar.gz: d6468040657b6ef60a75d68650c054966e359c9e16e076104e15022daf0ac41c
5
5
  SHA512:
6
- metadata.gz: 522eea93d714a4afe55e18f20be294674a3406915b31c858567471117aa6e9089d8a8269517efb6bd2b8c29a70d895109e1047a93c7ec68a739d856b8656d6a2
7
- data.tar.gz: 65e6082188ace39b2013404301fa137596b501d0f301fa0679697311038217642a08e29a7b5786fb406fdecf54016d90d49e6f652e90b89a9ae101b2db29448a
6
+ metadata.gz: c4d56fa78ad0f10d2d3cacf39277926636883acbba04c51f0bf85f5cc9888026f3ab4c50484c79a7d7d71e9a644c324ed8421e7a7c6ae5de407380fb114bf397
7
+ data.tar.gz: d53429a445fdc042ec7e73eb9a5f6fe80ec017314a85c287d42074e7fefca2fc67c0417e374effd23c858fa43685dd3916bded4522310ed8e9d58c7d5cca78f3
data/README.md CHANGED
@@ -33,25 +33,37 @@ $ gem install healthier
33
33
  ```
34
34
 
35
35
  This will generate these two files:
36
+
36
37
  1. config/healthier.yml
38
+
37
39
  ```yaml
38
40
  ---
39
41
  healthier:
40
42
  depends_on:
41
- # Uncomment accordingly
42
- # - name: 'postgresql'
43
- # - name: 'mongodb'
44
- # - name: 'redis'
45
- # - name: 'rabbitmq'
43
+ # Uncomment accordingly
44
+ # - name: 'postgresql'
45
+ # config: {}
46
+ # - name: 'mongodb'
47
+ # config: {}
48
+ # - name: 'redis'
49
+ # config: {}
50
+ # - name: 'rabbitmq'
51
+ # config: {}
46
52
  ```
53
+ NOTE: In future, config above will accept a hash each service's configuration
54
+
47
55
  2. config/initializers/healthier.rb
56
+
48
57
  ```ruby
49
58
  Healthier.setup do |config|
59
+ # If you don't specify auth_mechanism, then it defaults to http_basic.
60
+ # Currently, only http_basic and bearer_token are supported
61
+ config.auth_mechanism = 'bearer_token'
50
62
  config.depends_on = config.healthier.call
51
63
  end
52
64
  ```
53
65
 
54
- In your application routes.rb file:
66
+ In your application routes.rb file(namespace according to your need):
55
67
 
56
68
  ```ruby
57
69
  mount Healthier::Engine => '/healthier'
@@ -64,6 +76,23 @@ USERNAME: 'demouser'
64
76
  PASSWORD: 'demouser@2023'
65
77
  ```
66
78
 
79
+ ENVs for Bearer token authentication:
80
+
81
+ ```bash
82
+ BEARER_TOKEN: 'e66cfadb3ce37a714fdf3df237dcfded9ed9a0f9ccb09e6a744d731e9214bae6fac7bf6d27cab3969702b7aa54c81fe1cff1842ac03b3d5f1a918e1f5061dcb80b80363976ae5f3b916c793fdfa5283ce5f387286152d62e2dcaa5c8c0f857ceb2cda045'
83
+ ```
84
+
85
+ However, if you want to leave the API open, just set:
86
+
87
+ ```ruby
88
+ Healthier.auth_mechanism = :none
89
+ ```
90
+
91
+ Example Curl:
92
+ ```curl
93
+ curl -H "Bearer b5f3396a876e4f2ec828d11514c94d95d2f2330d251496a78cbe0e9bc330d775" -H "Content-Type: application/json" 'https://migros.selise.dev/api/v2/cnc_rsp/healthier/ping'
94
+ ```
95
+
67
96
  ## Contributing
68
97
 
69
98
  Contribution directions go here.
@@ -3,5 +3,6 @@
3
3
  module Healthier
4
4
  # ApplicationController
5
5
  class ApplicationController < ActionController::Base
6
+ include Errors::ApiErrors
6
7
  end
7
8
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Errors
4
+ module ApiErrors
5
+ def self.included(base)
6
+ base.class_eval do
7
+ rescue_from StandardError, with: :bad_request
8
+ end
9
+ end
10
+
11
+ def bad_request(error)
12
+ render_error(:bad_request, error: { message: error.message })
13
+ end
14
+
15
+ def render_success(status, options = {})
16
+ options[:success] = true
17
+ render json: options, status: status
18
+ end
19
+
20
+ def render_error(status, options = {})
21
+ options[:error] = { message: I18n.t("api_errors.#{status}") } if options[:error].blank?
22
+ options[:success] = false
23
+ Rollbar.error(options[:error])
24
+ render json: options, status: status
25
+ end
26
+
27
+ def render_silent_error(status, options = {})
28
+ options[:error] = { message: I18n.t("api_errors.#{status}") } if options[:error].blank?
29
+ options[:success] = false
30
+ render json: options, status: status
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Checkers
4
+ # Mongodb health checker
5
+ class MongodbChecker
6
+ class << self
7
+ def check
8
+ raise 'Please make sure you have Mongodb installed' unless defined?(::Mongo::Client)
9
+
10
+ begin
11
+ @client = Mongo::Client.new(
12
+ ENV.fetch('MONGO_DB_URI', nil) || ['localhost:27017'],
13
+ max_retries: 2,
14
+ retry_interval: 1
15
+ )
16
+ @client.command(ping: 1)
17
+ true
18
+ rescue Mongo::Error::SocketError, Mongo::Error::NoServerAvailable
19
+ false
20
+ ensure
21
+ @client&.close
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Checkers
4
+ # Postgres health checker
5
+ class PostgresqlChecker
6
+ class << self
7
+ def check
8
+ raise 'Please make sure you have Postgres installed' unless defined?(::ActiveRecord::Base)
9
+
10
+ begin
11
+ # @conn = ::PG.connect(dbname: ENV['DATABASE_NAME'])
12
+ # @conn.exec('SELECT 1')
13
+ @conn = ActiveRecord::Base.connection
14
+ true
15
+ rescue ::PG::ConnectionBad
16
+ false
17
+ ensure
18
+ @conn&.close
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Checkers
4
+ # Rabbitmq health checker
5
+ class RabbitmqChecker
6
+ class << self
7
+ def check
8
+ raise 'Please make sure you have RabbitMQ installed' unless defined?(::Bunny)
9
+
10
+ begin
11
+ @conn = ::Bunny.new
12
+ @conn.start
13
+ channel = @conn.create_channel
14
+ queue = channel.queue('health_check')
15
+ queue.publish('ping')
16
+ true
17
+ rescue ::Bunny::Exception, ::Bunny::TCPConnectionFailed, ::Bunny::PossibleAuthenticationFailureError
18
+ false
19
+ ensure
20
+ @conn&.close
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Checkers
4
+ # Redis health checker
5
+ class RedisChecker
6
+ class << self
7
+ def check
8
+ raise 'Please make sure you have Redis installed' unless defined?(::Redis)
9
+
10
+ begin
11
+ @redis = Redis.new
12
+ @redis.ping == 'PONG'
13
+ rescue Redis::CannotConnectError
14
+ false
15
+ ensure
16
+ @redis&.close
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -10,6 +10,9 @@ module Healthier
10
10
  # frozen_string_literal: true
11
11
 
12
12
  Healthier.setup do |config|
13
+ # Currently, there are :basic_auth(default), :bearer_token for API authentication
14
+ # But if you want to leave the API open, then just set auth_mechanism to :none to skip authentication
15
+ # config.auth_mechanism = :bearer_token
13
16
  config.depends_on = config.healthier.call
14
17
  end
15
18
  RUBY
@@ -22,9 +25,13 @@ module Healthier
22
25
  # Uncomment accordingly
23
26
  depends_on:
24
27
  # - name: 'postgresql'
28
+ # config: {}
25
29
  # - name: 'mongodb'
30
+ # config: {}
26
31
  # - name: 'redis'
32
+ # config: {}
27
33
  # - name: 'rabbitmq'
34
+ # config: {}
28
35
  YAML
29
36
  end
30
37
  end
@@ -3,25 +3,31 @@
3
3
  module Healthier
4
4
  # Engine
5
5
  class ApiAuthenticator
6
- attr_accessor :controller
6
+ attr_accessor :controller, :request
7
7
 
8
8
  def initialize(controller)
9
9
  @controller = controller
10
+ @request = controller.request
10
11
  end
11
12
 
12
13
  def authenticate
14
+ return true if Healthier.auth_mechanism == :none
15
+
13
16
  send("authenticate_with_#{Healthier.auth_mechanism}")
14
17
  end
15
18
 
16
19
  def authenticate_with_bearer_token
20
+ tok = request.authorization.to_s
21
+ raise StandardError, 'Access Denied' unless tok.starts_with?('Bearer')
22
+
17
23
  controller.authenticate_with_http_token do |token, _options|
18
- ENV['BEARER_TOKEN'] == token
24
+ raise StandardError, 'Invalid token' unless ENV['BEARER_TOKEN'] == token
19
25
  end
20
26
  end
21
27
 
22
28
  def authenticate_with_basic_auth
23
29
  controller.authenticate_or_request_with_http_basic do |username, password|
24
- username == ENV['USERNAME'] && password == ENV['PASSWORD']
30
+ raise StandardError, 'Invalid credentials' unless username == ENV['USERNAME'] && password == ENV['PASSWORD']
25
31
  end
26
32
  end
27
33
  end
@@ -1,35 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../checkers/mongodb_checker'
4
+ require_relative '../checkers/postgresql_checker'
5
+ require_relative '../checkers/rabbitmq_checker'
6
+ require_relative '../checkers/redis_checker'
7
+
3
8
  module Healthier
4
9
  # Engine
5
10
  class Engine < ::Rails::Engine
6
11
  isolate_namespace Healthier
7
12
 
8
- PING_CONF = HashWithIndifferentAccess.new(
9
- {
10
- postgresql: {
11
- pinger: -> { ActiveRecord::Base.connection },
12
- message: 'PostgreSQL service is not running. Please check your connection'
13
- },
14
- mongodb: {
15
- pinger: -> { Mongoid.default_client.command(ping: 1) },
16
- message: 'MongoDB service is not running. Please check your connection'
17
- },
18
- redis: {
19
- pinger: -> { Redis.new },
20
- message: 'Redis service is not running. Please check your connection'
21
- },
22
- rabbitmq: {
23
- pinger: -> { Bunny.new },
24
- message: 'RabbitMQ service is not running. Please check your connection'
25
- }
26
- }
27
- )
28
-
29
- # TODO: Refactor this whole code
30
- SUCCESS = 200
31
- FAILURE = 400
32
-
33
13
  def ping!
34
14
  conf['depends_on'].each_with_object({}) do |dependant, obj|
35
15
  obj[dependant['name']] = ping_it(dependant['name'])
@@ -41,9 +21,15 @@ module Healthier
41
21
  end
42
22
 
43
23
  def ping_it(service)
44
- return { status: SUCCESS, message: 'pong' } if PING_CONF[service]['pinger'].call
24
+ checked = checker(service).check
25
+
26
+ return { status: Healthier::SUCCESS, message: 'pong' } if checked == true
27
+
28
+ { status: Healthier::FAILURE, message: "#{service.camelize} service is not running. Please check your connection" }
29
+ end
45
30
 
46
- { status: FAILURE, message: PING_CONF[service]['message'] }
31
+ def checker(service)
32
+ "::Checkers::#{service.camelize}Checker".constantize
47
33
  end
48
34
  end
49
35
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Healthier
4
4
  # MAJOR.MINOR.PATCH
5
- VERSION = '0.1.5'
5
+ VERSION = '0.1.7'
6
6
  end
data/lib/healthier.rb CHANGED
@@ -10,6 +10,9 @@ module Healthier
10
10
  yield self
11
11
  end
12
12
 
13
+ SUCCESS = 200
14
+ FAILURE = 503
15
+
13
16
  mattr_accessor :healthier, default: -> { YAML.load_file(Rails.root.join('config/healthier.yml')) }
14
17
  mattr_accessor :auth_mechanism, default: 'basic_auth'
15
18
  mattr_accessor :depends_on, default: {}
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: healthier
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nima Yonten
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-03 00:00:00.000000000 Z
11
+ date: 2023-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -36,7 +36,12 @@ files:
36
36
  - Rakefile
37
37
  - app/controllers/healthier/application_controller.rb
38
38
  - app/controllers/healthier/checks_controller.rb
39
+ - app/lib/errors/api_errors.rb
39
40
  - config/routes.rb
41
+ - lib/checkers/mongodb_checker.rb
42
+ - lib/checkers/postgresql_checker.rb
43
+ - lib/checkers/rabbitmq_checker.rb
44
+ - lib/checkers/redis_checker.rb
40
45
  - lib/generators/healthier/config_generator.rb
41
46
  - lib/healthier.rb
42
47
  - lib/healthier/api_authenticator.rb
@@ -51,6 +56,7 @@ metadata:
51
56
  homepage_uri: https://github.com/selisebt/healthier
52
57
  source_code_uri: https://github.com/selisebt/healthier
53
58
  changelog_uri: https://github.com/selisebt/healthier
59
+ rubygems_mfa_required: 'true'
54
60
  post_install_message:
55
61
  rdoc_options: []
56
62
  require_paths: