healthier 0.2.3 → 0.2.5
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 +4 -4
- data/README.md +55 -5
- data/app/controllers/healthier/{checks_controller.rb → healthier_controller.rb} +12 -3
- data/config/routes.rb +3 -1
- data/lib/connectors/elastic_search.rb +36 -0
- data/lib/connectors/rabbitmq.rb +2 -1
- data/lib/connectors/sidekiq.rb +21 -0
- data/lib/generators/healthier/config_generator.rb +5 -0
- data/lib/healthier/api_authenticator.rb +2 -2
- data/lib/healthier/doctor.rb +98 -0
- data/lib/healthier/engine.rb +7 -29
- data/lib/healthier/version.rb +1 -1
- metadata +17 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f347f1274991b95aa7403f00501e5fc901d48ac74c9d6d6ae44c68a5ee7bdc6
|
|
4
|
+
data.tar.gz: c8f8f1b4fca3b876d3ab3d134f14bc5e46514f45211d7df81a22b8202f3872df
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ce524e72bfe8aec9444c0602d7488362047d0b0bb33e674e922f8398e942a626113b4241eed3647c820b1ddba1ecedcf2e3611698d936c91c38f5399c6840b4d
|
|
7
|
+
data.tar.gz: d07d6415672d8e0c28865813d29a8ef959aace1a0491a6c439ede5c283856a0f5c25aace7cf2955184a195dc633f247dfc531d34204705a3a35aa5a26aa640e6
|
data/README.md
CHANGED
|
@@ -72,8 +72,8 @@ mount Healthier::Engine => '/healthier'
|
|
|
72
72
|
ENVs for basic authentication:
|
|
73
73
|
|
|
74
74
|
```bash
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
HEALTHIER_USERNAME: 'demouser'
|
|
76
|
+
HEALTHIER_PASSWORD: 'demouser@2023'
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
ENVs for Bearer token authentication:
|
|
@@ -88,9 +88,59 @@ However, if you want to leave the API open, just set:
|
|
|
88
88
|
Healthier.auth_mechanism = :none
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
## Health Check Endpoints
|
|
92
|
+
|
|
93
|
+
Healthier provides three distinct health check endpoints for monitoring your application:
|
|
94
|
+
|
|
95
|
+
### 1. Ping Endpoint (`GET /healthier/ping`)
|
|
96
|
+
Returns detailed health status of all configured services.
|
|
97
|
+
|
|
98
|
+
**Response:** JSON object with service health statuses
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"postgresql": {
|
|
102
|
+
"status": "success",
|
|
103
|
+
"message": "PostgreSQL service is healthy"
|
|
104
|
+
},
|
|
105
|
+
"redis": {
|
|
106
|
+
"status": "success",
|
|
107
|
+
"message": "Redis service is healthy"
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 2. Live Endpoint (`GET /healthier/live`)
|
|
113
|
+
Basic liveness check that verifies if the application is running.
|
|
114
|
+
|
|
115
|
+
**Response:** Boolean value
|
|
116
|
+
```json
|
|
117
|
+
true
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 3. Ready Endpoint (`GET /healthier/ready`)
|
|
121
|
+
Readiness check that verifies if all configured services are healthy and the application is ready to serve requests.
|
|
122
|
+
|
|
123
|
+
**Response:** Boolean value
|
|
124
|
+
```json
|
|
125
|
+
true
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Example Usage
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Ping - detailed service health
|
|
132
|
+
curl http://localhost:3000/healthier/ping
|
|
133
|
+
|
|
134
|
+
# Live - basic liveness check
|
|
135
|
+
curl http://localhost:3000/healthier/live
|
|
136
|
+
|
|
137
|
+
# Ready - service readiness check
|
|
138
|
+
curl http://localhost:3000/healthier/ready
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Example Curl with authentication:
|
|
142
|
+
```bash
|
|
143
|
+
curl -H "Bearer your_token_here" -H "Content-Type: application/json" 'http://localhost:3000/healthier/ping'
|
|
94
144
|
```
|
|
95
145
|
|
|
96
146
|
## Contributing
|
|
@@ -2,13 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
module Healthier
|
|
4
4
|
# Controller for performing health checks
|
|
5
|
-
class
|
|
5
|
+
class HealthierController < ApplicationController
|
|
6
6
|
include ActionController::HttpAuthentication::Basic::ControllerMethods
|
|
7
7
|
|
|
8
|
-
before_action :authenticate_request
|
|
8
|
+
before_action :authenticate_request, except: [:live]
|
|
9
9
|
|
|
10
10
|
def ping
|
|
11
|
-
render json: Healthier::
|
|
11
|
+
render json: Healthier::Doctor.ping!
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# NOTE: Just return 200
|
|
15
|
+
def live
|
|
16
|
+
render json: Healthier::Doctor.up?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def ready
|
|
20
|
+
render json: Healthier::Doctor.ping!
|
|
12
21
|
end
|
|
13
22
|
|
|
14
23
|
protected
|
data/config/routes.rb
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Connectors
|
|
4
|
+
# ElasticSearch health checker
|
|
5
|
+
class ElasticSearch
|
|
6
|
+
def connect
|
|
7
|
+
url = ENV.fetch('ELASTICSEARCH_URL', 'http://localhost:9200')
|
|
8
|
+
|
|
9
|
+
require 'net/http'
|
|
10
|
+
require 'uri'
|
|
11
|
+
require 'json'
|
|
12
|
+
|
|
13
|
+
uri = URI.parse(url)
|
|
14
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
15
|
+
http.read_timeout = 2
|
|
16
|
+
http.open_timeout = 2
|
|
17
|
+
|
|
18
|
+
response = http.get('/_cluster/health')
|
|
19
|
+
if response.code.to_i == 200
|
|
20
|
+
health_data = JSON.parse(response.body)
|
|
21
|
+
health_data['status'] != 'red'
|
|
22
|
+
else
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
rescue StandardError => e
|
|
26
|
+
puts "ElasticSearch Connection Error: #{e.message}"
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.configured? # rubocop:disable Naming/PredicateMethod
|
|
31
|
+
raise StandardError, 'Please make sure you have Elasticsearch installed' unless defined?(::Elasticsearch::Client)
|
|
32
|
+
|
|
33
|
+
'configured'
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/connectors/rabbitmq.rb
CHANGED
|
@@ -7,13 +7,14 @@ module Connectors
|
|
|
7
7
|
@conn = ::Bunny.new
|
|
8
8
|
@conn.start
|
|
9
9
|
@channel = @conn.create_channel
|
|
10
|
-
@queue = @channel.queue('health_check')
|
|
10
|
+
@queue = @channel.queue('health_check', auto_delete: true)
|
|
11
11
|
@queue.publish('ping')
|
|
12
12
|
true
|
|
13
13
|
rescue ::Bunny::Exception, ::Bunny::TCPConnectionFailed, ::Bunny::PossibleAuthenticationFailureError => e
|
|
14
14
|
puts "RabbitMQ Connection Error: #{e.message}"
|
|
15
15
|
false
|
|
16
16
|
ensure
|
|
17
|
+
# Queue with auto_delete: true will be automatically deleted when connection closes.
|
|
17
18
|
@queue&.delete
|
|
18
19
|
@queue&.purge
|
|
19
20
|
@channel&.close
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Connectors
|
|
4
|
+
# Sidekiq health checker
|
|
5
|
+
class Sidekiq
|
|
6
|
+
def connect
|
|
7
|
+
::Sidekiq.redis do |r|
|
|
8
|
+
r.ping == 'PONG'
|
|
9
|
+
end
|
|
10
|
+
rescue StandardError => e
|
|
11
|
+
puts "Sidekiq Connection Error: #{e.message}"
|
|
12
|
+
false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.configured?
|
|
16
|
+
raise StandardError, 'Please make sure you have Sidekiq installed' unless defined?(::Sidekiq)
|
|
17
|
+
|
|
18
|
+
'configured'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -17,13 +17,13 @@ module Healthier
|
|
|
17
17
|
|
|
18
18
|
def authenticate_with_bearer_token
|
|
19
19
|
controller.authenticate_with_http_token do |token, _options|
|
|
20
|
-
ENV['
|
|
20
|
+
ENV['HEALTHIER_BEARER_TOKEN'] == token
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def authenticate_with_basic_auth
|
|
25
25
|
controller.authenticate_or_request_with_http_basic do |username, password|
|
|
26
|
-
username == ENV['
|
|
26
|
+
username == ENV['HEALTHIER_USERNAME'] && password == ENV['HEALTHIER_PASSWORD']
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Healthier
|
|
4
|
+
# Health Checker Doctor
|
|
5
|
+
class Doctor
|
|
6
|
+
SUCCESS = :success
|
|
7
|
+
FAILURE = :failure
|
|
8
|
+
|
|
9
|
+
def initialize(config = nil)
|
|
10
|
+
@config = config || Healthier.depends_on['healthier'] || {}
|
|
11
|
+
@connectors = load_connectors
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.ping!
|
|
15
|
+
new.ping
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.up?
|
|
19
|
+
new.up?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.running?
|
|
23
|
+
new.running?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def up?
|
|
27
|
+
# Basic health check - just return true for now
|
|
28
|
+
# Could be extended to check basic application health
|
|
29
|
+
true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def running?
|
|
33
|
+
# Check if all configured services are running
|
|
34
|
+
ping.values.all? { |service_result| service_result[:status] == SUCCESS }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def ping
|
|
38
|
+
@config.fetch('depends_on', []).each_with_object({}) do |service_config, result|
|
|
39
|
+
result[service_config['name']] = diagnose(service_config)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def load_connectors
|
|
46
|
+
@config.fetch('depends_on', []).to_h do |service_config|
|
|
47
|
+
[
|
|
48
|
+
service_config['name'],
|
|
49
|
+
"::Connectors::#{service_config['name'].camelize}".constantize
|
|
50
|
+
]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def diagnose(service_config)
|
|
55
|
+
connector_class = @connectors[service_config['name']]
|
|
56
|
+
|
|
57
|
+
return custom_diagnosis(service_config) if service_config['custom_checker']
|
|
58
|
+
|
|
59
|
+
begin
|
|
60
|
+
connector = connector_class.new
|
|
61
|
+
connection_result = connector.connect
|
|
62
|
+
|
|
63
|
+
if connection_result == true
|
|
64
|
+
{
|
|
65
|
+
status: SUCCESS,
|
|
66
|
+
message: "#{service_config['name'].camelize} service is healthy"
|
|
67
|
+
}
|
|
68
|
+
else
|
|
69
|
+
{
|
|
70
|
+
status: FAILURE,
|
|
71
|
+
message: "#{service_config['name'].camelize} service is not responding"
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
rescue StandardError => e
|
|
75
|
+
{
|
|
76
|
+
status: FAILURE,
|
|
77
|
+
message: "Diagnosis failed for #{service_config['name']}: #{e.message}"
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def custom_diagnosis(service_config)
|
|
83
|
+
custom_checker = service_config['custom_checker']
|
|
84
|
+
|
|
85
|
+
if custom_checker.respond_to?(:call)
|
|
86
|
+
custom_checker.call
|
|
87
|
+
elsif custom_checker.is_a?(String)
|
|
88
|
+
connector_class = @connectors[service_config['name']]
|
|
89
|
+
connector_class.send(custom_checker)
|
|
90
|
+
else
|
|
91
|
+
{
|
|
92
|
+
status: FAILURE,
|
|
93
|
+
message: "Invalid diagnosis method for #{service_config['name']}"
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
data/lib/healthier/engine.rb
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'doctor'
|
|
3
4
|
require_relative '../connectors/mongodb'
|
|
4
5
|
require_relative '../connectors/postgresql'
|
|
5
6
|
require_relative '../connectors/rabbitmq'
|
|
6
7
|
require_relative '../connectors/redis'
|
|
8
|
+
require_relative '../connectors/sidekiq'
|
|
9
|
+
require_relative '../connectors/elastic_search'
|
|
7
10
|
|
|
8
11
|
module Healthier
|
|
9
12
|
# Engine
|
|
@@ -13,38 +16,13 @@ module Healthier
|
|
|
13
16
|
initializer 'healthier.after_initialize' do
|
|
14
17
|
ActiveSupport.on_load(:after_initialize) do
|
|
15
18
|
dependent_services = Healthier.depends_on.dig('healthier', 'depends_on')
|
|
19
|
+
next unless dependent_services
|
|
20
|
+
|
|
16
21
|
dependent_services.each do |service_conf|
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
connector_class = "::Connectors::#{service_conf['name'].camelize}".constantize
|
|
23
|
+
connector_class.configured?
|
|
19
24
|
end
|
|
20
25
|
end
|
|
21
26
|
end
|
|
22
|
-
|
|
23
|
-
def ping!
|
|
24
|
-
conf['depends_on'].each_with_object({}) do |dependent, obj|
|
|
25
|
-
obj[dependent['name']] = ping_it(dependent['name'])
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def conf
|
|
30
|
-
@conf ||= Healthier.depends_on['healthier']
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def ping_it(service)
|
|
34
|
-
connector = "::Connectors::#{service.camelize}".constantize
|
|
35
|
-
connection = connector.new.connect
|
|
36
|
-
|
|
37
|
-
if connection == true
|
|
38
|
-
return {
|
|
39
|
-
status: Healthier::SUCCESS,
|
|
40
|
-
message: 'pong'
|
|
41
|
-
}
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
{
|
|
45
|
-
status: Healthier::FAILURE,
|
|
46
|
-
message: "#{service.camelize} service is not running. Please check your connection"
|
|
47
|
-
}
|
|
48
|
-
end
|
|
49
27
|
end
|
|
50
28
|
end
|
data/lib/healthier/version.rb
CHANGED
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.2.
|
|
4
|
+
version: 0.2.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nima Yonten
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2025-12-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -24,9 +24,12 @@ dependencies:
|
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '6.0'
|
|
27
|
-
description:
|
|
27
|
+
description: Healthier provides comprehensive health check endpoints (live, ready,
|
|
28
|
+
ping) for Rails applications with support for monitoring multiple backing services
|
|
29
|
+
including PostgreSQL, MongoDB, Redis, RabbitMQ, Sidekiq, and ElasticSearch. It includes
|
|
30
|
+
built-in authentication and flexible configuration options.
|
|
28
31
|
email:
|
|
29
|
-
-
|
|
32
|
+
- bugloper@hey.com
|
|
30
33
|
executables: []
|
|
31
34
|
extensions: []
|
|
32
35
|
extra_rdoc_files: []
|
|
@@ -35,15 +38,18 @@ files:
|
|
|
35
38
|
- README.md
|
|
36
39
|
- Rakefile
|
|
37
40
|
- app/controllers/healthier/application_controller.rb
|
|
38
|
-
- app/controllers/healthier/
|
|
41
|
+
- app/controllers/healthier/healthier_controller.rb
|
|
39
42
|
- config/routes.rb
|
|
43
|
+
- lib/connectors/elastic_search.rb
|
|
40
44
|
- lib/connectors/mongodb.rb
|
|
41
45
|
- lib/connectors/postgresql.rb
|
|
42
46
|
- lib/connectors/rabbitmq.rb
|
|
43
47
|
- lib/connectors/redis.rb
|
|
48
|
+
- lib/connectors/sidekiq.rb
|
|
44
49
|
- lib/generators/healthier/config_generator.rb
|
|
45
50
|
- lib/healthier.rb
|
|
46
51
|
- lib/healthier/api_authenticator.rb
|
|
52
|
+
- lib/healthier/doctor.rb
|
|
47
53
|
- lib/healthier/engine.rb
|
|
48
54
|
- lib/healthier/version.rb
|
|
49
55
|
- lib/tasks/healthier_tasks.rake
|
|
@@ -55,8 +61,8 @@ metadata:
|
|
|
55
61
|
homepage_uri: https://github.com/SELISEdigitalplatforms/healthier
|
|
56
62
|
source_code_uri: https://github.com/SELISEdigitalplatforms/healthier
|
|
57
63
|
changelog_uri: https://github.com/SELISEdigitalplatforms/healthier
|
|
58
|
-
rubygems_mfa_required: '
|
|
59
|
-
post_install_message:
|
|
64
|
+
rubygems_mfa_required: 'false'
|
|
65
|
+
post_install_message:
|
|
60
66
|
rdoc_options: []
|
|
61
67
|
require_paths:
|
|
62
68
|
- lib
|
|
@@ -71,8 +77,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
71
77
|
- !ruby/object:Gem::Version
|
|
72
78
|
version: '0'
|
|
73
79
|
requirements: []
|
|
74
|
-
rubygems_version: 3.4.
|
|
75
|
-
signing_key:
|
|
80
|
+
rubygems_version: 3.4.1
|
|
81
|
+
signing_key:
|
|
76
82
|
specification_version: 4
|
|
77
|
-
summary:
|
|
83
|
+
summary: Healthier is a gem that provides a health check endpoint for your Rails application.
|
|
78
84
|
test_files: []
|