rails_health_check 3.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.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +49 -0
- data/.travis.yml +50 -0
- data/CHANGELOG +64 -0
- data/Gemfile +20 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +337 -0
- data/Rakefile +29 -0
- data/Vagrantfile +20 -0
- data/config/routes.rb +5 -0
- data/health_check.gemspec +32 -0
- data/init.rb +2 -0
- data/lib/health_check/base_health_check.rb +5 -0
- data/lib/health_check/health_check_controller.rb +75 -0
- data/lib/health_check/health_check_routes.rb +15 -0
- data/lib/health_check/middleware_health_check.rb +102 -0
- data/lib/health_check/redis_health_check.rb +23 -0
- data/lib/health_check/resque_health_check.rb +15 -0
- data/lib/health_check/s3_health_check.rb +65 -0
- data/lib/health_check/sidekiq_health_check.rb +17 -0
- data/lib/health_check/utils.rb +171 -0
- data/lib/health_check/version.rb +3 -0
- data/lib/health_check.rb +101 -0
- data/test/fake_smtp_server +38 -0
- data/test/init_variables +61 -0
- data/test/migrate/empty/do_not_remove.txt +1 -0
- data/test/migrate/nine/9_create_countries.rb +11 -0
- data/test/migrate/twelve/011_create_roles.roles.rb +11 -0
- data/test/migrate/twelve/012_create_users.rb +11 -0
- data/test/migrate/twelve/9_create_countries.rb +11 -0
- data/test/provision_vagrant +61 -0
- data/test/rails_5.1.gemfile +26 -0
- data/test/rails_edge.gemfile +32 -0
- data/test/setup_railsapp +440 -0
- data/test/test_with_railsapp +679 -0
- data/test/testurl +65 -0
- metadata +180 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
# Copyright (c) 2010-2013 Ian Heggie, released under the MIT license.
|
2
|
+
# See MIT-LICENSE for details.
|
3
|
+
|
4
|
+
module HealthCheck
|
5
|
+
class HealthCheckController < ActionController::Base
|
6
|
+
|
7
|
+
layout false if self.respond_to? :layout
|
8
|
+
before_action :check_origin_ip
|
9
|
+
before_action :authenticate
|
10
|
+
|
11
|
+
def index
|
12
|
+
last_modified = Time.now.utc
|
13
|
+
max_age = HealthCheck.max_age
|
14
|
+
if max_age > 1
|
15
|
+
last_modified = Time.at((last_modified.to_f / max_age).floor * max_age).utc
|
16
|
+
end
|
17
|
+
public = (max_age > 1) && ! HealthCheck.basic_auth_username
|
18
|
+
if stale?(:last_modified => last_modified, :public => public)
|
19
|
+
checks = params[:checks] ? params[:checks].split('_') : ['standard']
|
20
|
+
checks -= HealthCheck.middleware_checks if HealthCheck.installed_as_middleware
|
21
|
+
begin
|
22
|
+
errors = HealthCheck::Utils.process_checks(checks)
|
23
|
+
rescue Exception => e
|
24
|
+
errors = e.message.blank? ? e.class.to_s : e.message.to_s
|
25
|
+
end
|
26
|
+
response.headers['Cache-control'] = (public ? 'public' : 'private') + ', no-cache, must-revalidate' + (max_age > 0 ? ", max-age=#{max_age}" : '')
|
27
|
+
if errors.blank?
|
28
|
+
send_response true, nil, :ok, :ok
|
29
|
+
else
|
30
|
+
msg = HealthCheck.include_error_in_response_body ? "health_check failed: #{errors}" : nil
|
31
|
+
send_response false, msg, HealthCheck.http_status_for_error_text, HealthCheck.http_status_for_error_object
|
32
|
+
# Log a single line as some uptime checkers only record that it failed, not the text returned
|
33
|
+
if logger
|
34
|
+
logger.info msg
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def send_response(healthy, msg, text_status, obj_status)
|
43
|
+
msg ||= healthy ? HealthCheck.success : HealthCheck.failure
|
44
|
+
obj = { :healthy => healthy, :message => msg}
|
45
|
+
respond_to do |format|
|
46
|
+
format.html { render :plain => msg, :status => text_status, :content_type => 'text/plain' }
|
47
|
+
format.json { render :json => obj, :status => obj_status }
|
48
|
+
format.xml { render :xml => obj, :status => obj_status }
|
49
|
+
format.any { render :plain => msg, :status => text_status, :content_type => 'text/plain' }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def authenticate
|
54
|
+
return unless HealthCheck.basic_auth_username && HealthCheck.basic_auth_password
|
55
|
+
authenticate_or_request_with_http_basic('Health Check') do |username, password|
|
56
|
+
username == HealthCheck.basic_auth_username && password == HealthCheck.basic_auth_password
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def check_origin_ip
|
61
|
+
unless HealthCheck.origin_ip_whitelist.blank? ||
|
62
|
+
HealthCheck.origin_ip_whitelist.include?(request.ip)
|
63
|
+
render :plain => 'Health check is not allowed for the requesting IP',
|
64
|
+
:status => HealthCheck.http_status_for_ip_whitelist_error,
|
65
|
+
:content_type => 'text/plain'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# turn cookies for CSRF off
|
70
|
+
def protect_against_forgery?
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ActionDispatch::Routing
|
2
|
+
class Mapper
|
3
|
+
|
4
|
+
def health_check_routes(prefix = nil)
|
5
|
+
HealthCheck::Engine.routes_explicitly_defined = true
|
6
|
+
add_health_check_routes(prefix)
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_health_check_routes(prefix = nil)
|
10
|
+
HealthCheck.uri = prefix if prefix
|
11
|
+
match "#{HealthCheck.uri}(/:checks)(.:format)", :to => 'health_check/health_check#index', via: [:get, :post], :defaults => { :format => 'txt' }
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module HealthCheck
|
2
|
+
class MiddlewareHealthcheck
|
3
|
+
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
(response_type, middleware_checks, full_stack_checks) = parse_env(env)
|
10
|
+
if response_type
|
11
|
+
if error_response = (ip_blocked(env) || not_authenticated(env))
|
12
|
+
return error_response
|
13
|
+
end
|
14
|
+
HealthCheck.installed_as_middleware = true
|
15
|
+
errors = ''
|
16
|
+
begin
|
17
|
+
# Process the checks to be run from middleware
|
18
|
+
errors = HealthCheck::Utils.process_checks(middleware_checks, true)
|
19
|
+
# Process remaining checks through the full stack if there are any
|
20
|
+
unless full_stack_checks.empty?
|
21
|
+
return @app.call(env)
|
22
|
+
end
|
23
|
+
rescue => e
|
24
|
+
errors = e.message.blank? ? e.class.to_s : e.message.to_s
|
25
|
+
end
|
26
|
+
healthy = errors.blank?
|
27
|
+
msg = healthy ? HealthCheck.success : "health_check failed: #{errors}"
|
28
|
+
if response_type == 'xml'
|
29
|
+
content_type = 'text/xml'
|
30
|
+
msg = { healthy: healthy, message: msg }.to_xml
|
31
|
+
error_code = HealthCheck.http_status_for_error_object
|
32
|
+
elsif response_type == 'json'
|
33
|
+
content_type = 'application/json'
|
34
|
+
msg = { healthy: healthy, message: msg }.to_json
|
35
|
+
error_code = HealthCheck.http_status_for_error_object
|
36
|
+
else
|
37
|
+
content_type = 'text/plain'
|
38
|
+
error_code = HealthCheck.http_status_for_error_text
|
39
|
+
end
|
40
|
+
[ (healthy ? 200 : error_code), { 'Content-Type' => content_type }, [msg] ]
|
41
|
+
else
|
42
|
+
@app.call(env)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def parse_env(env)
|
49
|
+
uri = env['PATH_INFO']
|
50
|
+
if uri =~ /^\/#{Regexp.escape HealthCheck.uri}(\/([-_0-9a-zA-Z]*))?(\.(\w*))?$/
|
51
|
+
checks = $2.to_s == '' ? ['standard'] : $2.split('_')
|
52
|
+
response_type = $4.to_s
|
53
|
+
middleware_checks = checks & HealthCheck.middleware_checks
|
54
|
+
full_stack_checks = (checks - HealthCheck.middleware_checks) - ['and']
|
55
|
+
[response_type, middleware_checks, full_stack_checks ]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def ip_blocked(env)
|
60
|
+
return false if HealthCheck.origin_ip_whitelist.blank?
|
61
|
+
req = Rack::Request.new(env)
|
62
|
+
unless HealthCheck.origin_ip_whitelist.include?(req.ip)
|
63
|
+
[ HealthCheck.http_status_for_ip_whitelist_error,
|
64
|
+
{ 'Content-Type' => 'text/plain' },
|
65
|
+
[ 'Health check is not allowed for the requesting IP' ]
|
66
|
+
]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def not_authenticated(env)
|
71
|
+
return false unless HealthCheck.basic_auth_username && HealthCheck.basic_auth_password
|
72
|
+
auth = MiddlewareHealthcheck::Request.new(env)
|
73
|
+
if auth.provided? && auth.basic? && Rack::Utils.secure_compare(HealthCheck.basic_auth_username, auth.username) && Rack::Utils.secure_compare(HealthCheck.basic_auth_password, auth.password)
|
74
|
+
env['REMOTE_USER'] = auth.username
|
75
|
+
return false
|
76
|
+
end
|
77
|
+
[ 401,
|
78
|
+
{ 'Content-Type' => 'text/plain', 'WWW-Authenticate' => 'Basic realm="Health Check"' },
|
79
|
+
[ ]
|
80
|
+
]
|
81
|
+
end
|
82
|
+
|
83
|
+
class Request < Rack::Auth::AbstractRequest
|
84
|
+
def basic?
|
85
|
+
"basic" == scheme
|
86
|
+
end
|
87
|
+
|
88
|
+
def credentials
|
89
|
+
@credentials ||= params.unpack("m*").first.split(/:/, 2)
|
90
|
+
end
|
91
|
+
|
92
|
+
def username
|
93
|
+
credentials.first
|
94
|
+
end
|
95
|
+
|
96
|
+
def password
|
97
|
+
credentials.last
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HealthCheck
|
4
|
+
class RedisHealthCheck
|
5
|
+
extend BaseHealthCheck
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def check
|
9
|
+
raise "Wrong configuration. Missing 'redis' gem" unless defined?(::Redis)
|
10
|
+
|
11
|
+
client.ping == 'PONG' ? '' : "Redis.ping returned #{res.inspect} instead of PONG"
|
12
|
+
rescue Exception => err
|
13
|
+
create_error 'redis', err.message
|
14
|
+
ensure
|
15
|
+
client.disconnect
|
16
|
+
end
|
17
|
+
|
18
|
+
def client
|
19
|
+
Redis.new(url: HealthCheck.redis_url)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module HealthCheck
|
2
|
+
class ResqueHealthCheck
|
3
|
+
extend BaseHealthCheck
|
4
|
+
|
5
|
+
def self.check
|
6
|
+
unless defined?(::Resque)
|
7
|
+
raise "Wrong configuration. Missing 'resque' gem"
|
8
|
+
end
|
9
|
+
res = ::Resque.redis.ping
|
10
|
+
res == 'PONG' ? '' : "Resque.redis.ping returned #{res.inspect} instead of PONG"
|
11
|
+
rescue Exception => e
|
12
|
+
create_error 'resque-redis', e.message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module HealthCheck
|
2
|
+
class S3HealthCheck
|
3
|
+
extend BaseHealthCheck
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def check
|
7
|
+
unless defined?(::Aws)
|
8
|
+
raise "Wrong configuration. Missing 'aws-sdk' gem"
|
9
|
+
end
|
10
|
+
return create_error 's3', 'Could not connect to aws' if aws_s3_client.nil?
|
11
|
+
HealthCheck.buckets.each do |bucket_name, permissions|
|
12
|
+
if permissions.nil? # backward compatible
|
13
|
+
permissions = [:R, :W, :D]
|
14
|
+
end
|
15
|
+
permissions.each do |permision|
|
16
|
+
begin
|
17
|
+
send(permision, bucket_name)
|
18
|
+
rescue Exception => e
|
19
|
+
raise "bucket:#{bucket_name}, permission:#{permision} - #{e.message}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
''
|
24
|
+
rescue Exception => e
|
25
|
+
create_error 's3', e.message
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def configure_client
|
31
|
+
return unless defined?(Rails)
|
32
|
+
|
33
|
+
aws_configuration = {
|
34
|
+
region: Rails.application.secrets.aws_default_region,
|
35
|
+
credentials: ::Aws::Credentials.new(
|
36
|
+
Rails.application.secrets.aws_access_key_id,
|
37
|
+
Rails.application.secrets.aws_secret_access_key
|
38
|
+
),
|
39
|
+
force_path_style: true
|
40
|
+
}
|
41
|
+
|
42
|
+
::Aws::S3::Client.new aws_configuration
|
43
|
+
end
|
44
|
+
|
45
|
+
def aws_s3_client
|
46
|
+
@aws_s3_client ||= configure_client
|
47
|
+
end
|
48
|
+
|
49
|
+
def R(bucket)
|
50
|
+
aws_s3_client.list_objects(bucket: bucket)
|
51
|
+
end
|
52
|
+
|
53
|
+
def W(bucket)
|
54
|
+
aws_s3_client.put_object(bucket: bucket,
|
55
|
+
key: "healthcheck_#{Rails.application.class.parent_name}",
|
56
|
+
body: Time.new.to_s)
|
57
|
+
end
|
58
|
+
|
59
|
+
def D(bucket)
|
60
|
+
aws_s3_client.delete_object(bucket: bucket,
|
61
|
+
key: "healthcheck_#{Rails.application.class.parent_name}")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module HealthCheck
|
2
|
+
class SidekiqHealthCheck
|
3
|
+
extend BaseHealthCheck
|
4
|
+
|
5
|
+
def self.check
|
6
|
+
unless defined?(::Sidekiq)
|
7
|
+
raise "Wrong configuration. Missing 'sidekiq' gem"
|
8
|
+
end
|
9
|
+
::Sidekiq.redis do |r|
|
10
|
+
res = r.ping
|
11
|
+
res == 'PONG' ? '' : "Sidekiq.redis.ping returned #{res.inspect} instead of PONG"
|
12
|
+
end
|
13
|
+
rescue Exception => e
|
14
|
+
create_error 'sidekiq-redis', e.message
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# Copyright (c) 2010-2013 Ian Heggie, released under the MIT license.
|
2
|
+
# See MIT-LICENSE for details.
|
3
|
+
|
4
|
+
module HealthCheck
|
5
|
+
class Utils
|
6
|
+
|
7
|
+
@@default_smtp_settings =
|
8
|
+
{
|
9
|
+
:address => "localhost",
|
10
|
+
:port => 25,
|
11
|
+
:domain => 'localhost.localdomain',
|
12
|
+
:user_name => nil,
|
13
|
+
:password => nil,
|
14
|
+
:authentication => nil,
|
15
|
+
:enable_starttls_auto => true,
|
16
|
+
}
|
17
|
+
|
18
|
+
cattr_accessor :default_smtp_settings
|
19
|
+
|
20
|
+
# process an array containing a list of checks
|
21
|
+
def self.process_checks(checks, called_from_middleware = false)
|
22
|
+
errors = ''
|
23
|
+
checks.each do |check|
|
24
|
+
case check
|
25
|
+
when 'and', 'site'
|
26
|
+
# do nothing
|
27
|
+
when "database"
|
28
|
+
HealthCheck::Utils.get_database_version
|
29
|
+
when "email"
|
30
|
+
errors << HealthCheck::Utils.check_email
|
31
|
+
when "emailconf"
|
32
|
+
errors << HealthCheck::Utils.check_email if HealthCheck::Utils.mailer_configured?
|
33
|
+
when "migrations", "migration"
|
34
|
+
if defined?(ActiveRecord::Migration) and ActiveRecord::Migration.respond_to?(:check_pending!)
|
35
|
+
# Rails 4+
|
36
|
+
begin
|
37
|
+
ActiveRecord::Migration.check_pending!
|
38
|
+
rescue ActiveRecord::PendingMigrationError => ex
|
39
|
+
errors << ex.message
|
40
|
+
end
|
41
|
+
else
|
42
|
+
database_version = HealthCheck::Utils.get_database_version
|
43
|
+
migration_version = HealthCheck::Utils.get_migration_version
|
44
|
+
if database_version.to_i != migration_version.to_i
|
45
|
+
errors << "Current database version (#{database_version}) does not match latest migration (#{migration_version}). "
|
46
|
+
end
|
47
|
+
end
|
48
|
+
when 'cache'
|
49
|
+
errors << HealthCheck::Utils.check_cache
|
50
|
+
when 'resque-redis-if-present'
|
51
|
+
errors << HealthCheck::ResqueHealthCheck.check if defined?(::Resque)
|
52
|
+
when 'sidekiq-redis-if-present'
|
53
|
+
errors << HealthCheck::SidekiqHealthCheck.check if defined?(::Sidekiq)
|
54
|
+
when 'redis-if-present'
|
55
|
+
errors << HealthCheck::RedisHealthCheck.check if defined?(::Redis)
|
56
|
+
when 's3-if-present'
|
57
|
+
errors << HealthCheck::S3HealthCheck.check if defined?(::Aws)
|
58
|
+
when 'resque-redis'
|
59
|
+
errors << HealthCheck::ResqueHealthCheck.check
|
60
|
+
when 'sidekiq-redis'
|
61
|
+
errors << HealthCheck::SidekiqHealthCheck.check
|
62
|
+
when 'redis'
|
63
|
+
errors << HealthCheck::RedisHealthCheck.check
|
64
|
+
when 's3'
|
65
|
+
errors << HealthCheck::S3HealthCheck.check
|
66
|
+
when "standard"
|
67
|
+
errors << HealthCheck::Utils.process_checks(HealthCheck.standard_checks, called_from_middleware)
|
68
|
+
when "middleware"
|
69
|
+
errors << "Health check not called from middleware - probably not installed as middleware." unless called_from_middleware
|
70
|
+
when "custom"
|
71
|
+
HealthCheck.custom_checks.each do |name, list|
|
72
|
+
list.each do |custom_check|
|
73
|
+
errors << custom_check.call(self)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
when "all", "full"
|
77
|
+
errors << HealthCheck::Utils.process_checks(HealthCheck.full_checks, called_from_middleware)
|
78
|
+
else
|
79
|
+
if HealthCheck.custom_checks.include? check
|
80
|
+
HealthCheck.custom_checks[check].each do |custom_check|
|
81
|
+
errors << custom_check.call(self)
|
82
|
+
end
|
83
|
+
else
|
84
|
+
return "invalid argument to health_test."
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
return errors
|
89
|
+
rescue => e
|
90
|
+
return e.message
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.db_migrate_path
|
94
|
+
# Lazy initialisation so Rails.root will be defined
|
95
|
+
@@db_migrate_path ||= File.join(Rails.root, 'db', 'migrate')
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.db_migrate_path=(value)
|
99
|
+
@@db_migrate_path = value
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.mailer_configured?
|
103
|
+
defined?(ActionMailer::Base) && (ActionMailer::Base.delivery_method != :smtp || HealthCheck::Utils.default_smtp_settings != ActionMailer::Base.smtp_settings)
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.get_database_version
|
107
|
+
ActiveRecord::Migrator.current_version if defined?(ActiveRecord)
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.get_migration_version(dir = self.db_migrate_path)
|
111
|
+
latest_migration = nil
|
112
|
+
Dir[File.join(dir, "[0-9]*_*.rb")].each do |f|
|
113
|
+
l = f.scan(/0*([0-9]+)_[_.a-zA-Z0-9]*.rb/).first.first rescue -1
|
114
|
+
latest_migration = l if !latest_migration || l.to_i > latest_migration.to_i
|
115
|
+
end
|
116
|
+
latest_migration
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.check_email
|
120
|
+
case ActionMailer::Base.delivery_method
|
121
|
+
when :smtp
|
122
|
+
HealthCheck::Utils.check_smtp(ActionMailer::Base.smtp_settings, HealthCheck.smtp_timeout)
|
123
|
+
when :sendmail
|
124
|
+
HealthCheck::Utils.check_sendmail(ActionMailer::Base.sendmail_settings)
|
125
|
+
else
|
126
|
+
''
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.check_sendmail(settings)
|
131
|
+
File.executable?(settings[:location]) ? '' : 'no sendmail executable found. '
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.check_smtp(settings, timeout)
|
135
|
+
status = ''
|
136
|
+
begin
|
137
|
+
if @skip_external_checks
|
138
|
+
status = '221'
|
139
|
+
else
|
140
|
+
Timeout::timeout(timeout) do |timeout_length|
|
141
|
+
t = TCPSocket.new(settings[:address], settings[:port])
|
142
|
+
begin
|
143
|
+
status = t.gets
|
144
|
+
while status != nil && status !~ /^2/
|
145
|
+
status = t.gets
|
146
|
+
end
|
147
|
+
t.puts "HELO #{settings[:domain]}\r"
|
148
|
+
while status != nil && status !~ /^250/
|
149
|
+
status = t.gets
|
150
|
+
end
|
151
|
+
t.puts "QUIT\r"
|
152
|
+
status = t.gets
|
153
|
+
ensure
|
154
|
+
t.close
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
rescue Errno::EBADF => ex
|
159
|
+
status = "Unable to connect to service"
|
160
|
+
rescue Exception => ex
|
161
|
+
status = ex.to_s
|
162
|
+
end
|
163
|
+
(status =~ /^221/) ? '' : "SMTP: #{status || 'unexpected EOF on socket'}. "
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.check_cache
|
167
|
+
Rails.cache.write('__health_check_cache_test__', 'ok', :expires_in => 1.second) ? '' : 'Unable to write to cache. '
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
end
|
data/lib/health_check.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# Copyright (c) 2010-2013 Ian Heggie, released under the MIT license.
|
2
|
+
# See MIT-LICENSE for details.
|
3
|
+
|
4
|
+
module HealthCheck
|
5
|
+
|
6
|
+
class Engine < Rails::Engine
|
7
|
+
cattr_accessor :routes_explicitly_defined
|
8
|
+
end
|
9
|
+
|
10
|
+
# Text output upon success
|
11
|
+
mattr_accessor :success
|
12
|
+
self.success = "success"
|
13
|
+
|
14
|
+
# Text output upon failure
|
15
|
+
mattr_accessor :failure
|
16
|
+
self.failure = "failure"
|
17
|
+
|
18
|
+
# Timeout in seconds used when checking smtp server
|
19
|
+
mattr_accessor :smtp_timeout
|
20
|
+
self.smtp_timeout = 30.0
|
21
|
+
|
22
|
+
# http status code used when plain text error message is output
|
23
|
+
mattr_accessor :http_status_for_error_text
|
24
|
+
self.http_status_for_error_text = 500
|
25
|
+
|
26
|
+
# http status code used when an error object is output (json or xml)
|
27
|
+
mattr_accessor :http_status_for_error_object
|
28
|
+
self.http_status_for_error_object = 500
|
29
|
+
|
30
|
+
# http status code used when the ip is not allowed for the request
|
31
|
+
mattr_accessor :http_status_for_ip_whitelist_error
|
32
|
+
self.http_status_for_ip_whitelist_error = 403
|
33
|
+
|
34
|
+
# ips allowed to perform requests
|
35
|
+
mattr_accessor :origin_ip_whitelist
|
36
|
+
self.origin_ip_whitelist = []
|
37
|
+
|
38
|
+
# max-age of response in seconds
|
39
|
+
# cache-control is public when max_age > 1 and basic authentication is used
|
40
|
+
mattr_accessor :max_age
|
41
|
+
self.max_age = 1
|
42
|
+
|
43
|
+
# s3 buckets
|
44
|
+
mattr_accessor :buckets
|
45
|
+
self.buckets = {}
|
46
|
+
|
47
|
+
# health check uri path
|
48
|
+
mattr_accessor :uri
|
49
|
+
self.uri = 'health_check'
|
50
|
+
|
51
|
+
# Basic Authentication
|
52
|
+
mattr_accessor :basic_auth_username, :basic_auth_password
|
53
|
+
self.basic_auth_username = nil
|
54
|
+
self.basic_auth_password = nil
|
55
|
+
|
56
|
+
# Array of custom check blocks
|
57
|
+
mattr_accessor :custom_checks
|
58
|
+
mattr_accessor :full_checks
|
59
|
+
mattr_accessor :standard_checks
|
60
|
+
self.custom_checks = { }
|
61
|
+
self.full_checks = ['database', 'migrations', 'custom', 'email', 'cache', 'redis-if-present', 'sidekiq-redis-if-present', 'resque-redis-if-present', 's3-if-present']
|
62
|
+
self.standard_checks = [ 'database', 'migrations', 'custom', 'emailconf' ]
|
63
|
+
|
64
|
+
# Middleware based checks
|
65
|
+
mattr_accessor :middleware_checks
|
66
|
+
self.middleware_checks = [ 'middleware' ]
|
67
|
+
|
68
|
+
mattr_accessor :installed_as_middleware
|
69
|
+
|
70
|
+
# Allow non-standard redis url
|
71
|
+
mattr_accessor :redis_url
|
72
|
+
self.redis_url = nil
|
73
|
+
|
74
|
+
# Include the error in the response body. You may want to set this to false
|
75
|
+
# if your /health_check endpoint is open to the public internet
|
76
|
+
mattr_accessor :include_error_in_response_body
|
77
|
+
self.include_error_in_response_body = true
|
78
|
+
|
79
|
+
def self.add_custom_check(name = 'custom', &block)
|
80
|
+
custom_checks[name] ||= [ ]
|
81
|
+
custom_checks[name] << block
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.setup
|
85
|
+
yield self
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
require 'health_check/version'
|
91
|
+
require 'health_check/base_health_check'
|
92
|
+
require 'health_check/resque_health_check'
|
93
|
+
require 'health_check/s3_health_check'
|
94
|
+
require 'health_check/redis_health_check'
|
95
|
+
require 'health_check/sidekiq_health_check'
|
96
|
+
require 'health_check/utils'
|
97
|
+
require 'health_check/health_check_controller'
|
98
|
+
require 'health_check/health_check_routes'
|
99
|
+
require 'health_check/middleware_health_check'
|
100
|
+
|
101
|
+
# vi: sw=2 sm ai:
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
port = 3555
|
6
|
+
|
7
|
+
|
8
|
+
server = TCPServer.new port
|
9
|
+
puts "fake_smtp_server: Waiting for one connection to port #{port} ..."
|
10
|
+
|
11
|
+
def send(client, line)
|
12
|
+
client.puts line
|
13
|
+
puts "> #{line}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def receive(client)
|
17
|
+
line = client.gets
|
18
|
+
puts "< #{line}"
|
19
|
+
line
|
20
|
+
end
|
21
|
+
|
22
|
+
client = server.accept # Wait for a client to connect
|
23
|
+
send(client, "220 dummy-smtp.example.com SMTP")
|
24
|
+
cmd = receive(client)
|
25
|
+
|
26
|
+
while cmd !~ /^QUIT\r/
|
27
|
+
if cmd =~ /^HELO(.*)\r/
|
28
|
+
send(client, "250 Welcome to a dummy smtp server")
|
29
|
+
else
|
30
|
+
send(client, "502 I am so dumb I only understand HELO and QUIT")
|
31
|
+
end
|
32
|
+
cmd = receive(client)
|
33
|
+
end
|
34
|
+
send(client, "221 Bye Bye")
|
35
|
+
|
36
|
+
client.close
|
37
|
+
puts "fake_smtp_server: Exiting now the conversation has finished."
|
38
|
+
exit 0
|
data/test/init_variables
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Any failure causes exit
|
4
|
+
set -e
|
5
|
+
|
6
|
+
echo Setting RAILS_ENV=test RACK_ENV=test
|
7
|
+
export RAILS_ENV=test RACK_ENV=test
|
8
|
+
|
9
|
+
base_dir=$PWD
|
10
|
+
tmp_dir=$base_dir/tmp
|
11
|
+
railsapp=$tmp_dir/railsapp
|
12
|
+
custom_file="$railsapp/tmp/custom_check.ok"
|
13
|
+
catchall_file="$railsapp/tmp/catchall_route.enabled"
|
14
|
+
success=successful
|
15
|
+
|
16
|
+
rehash=''
|
17
|
+
rbenv_which='which'
|
18
|
+
|
19
|
+
if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
|
20
|
+
echo "Detected user installed rvm"
|
21
|
+
elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
|
22
|
+
echo "Detected root installed rvm"
|
23
|
+
elif [[ -d "$HOME/.rbenv" ]] ; then
|
24
|
+
echo "Detected rbenv: `rbenv version`"
|
25
|
+
rehash='rbenv rehash'
|
26
|
+
rbenv_which='rbenv which'
|
27
|
+
else
|
28
|
+
printf "Note: Neither rvm nor rbenv was not found.\n"
|
29
|
+
fi
|
30
|
+
|
31
|
+
echo "Checking required commands exist:"
|
32
|
+
for cmd in bash gem egrep ls tail kill find cpio
|
33
|
+
do
|
34
|
+
echo -n " "
|
35
|
+
which $cmd || ( echo "Aborting setup_railsapp: Missing $cmd command!" && exit 2 )
|
36
|
+
done
|
37
|
+
for cmd in ruby gem
|
38
|
+
do
|
39
|
+
echo -n " "
|
40
|
+
$rbenv_which $cmd || ( echo "Aborting setup_railsapp: Missing $cmd command!" && exit 2 )
|
41
|
+
done
|
42
|
+
|
43
|
+
rails="rails"
|
44
|
+
rake="rake"
|
45
|
+
|
46
|
+
if [ -x $base_dir/test/bin/rails ]
|
47
|
+
then
|
48
|
+
rails="$base_dir/test/bin/rails"
|
49
|
+
rake="$base_dir/test/bin/rake"
|
50
|
+
export PATH="$base_dir/test/bin:$PATH"
|
51
|
+
fi
|
52
|
+
|
53
|
+
if [ -x $railsapp/bin/rails ]
|
54
|
+
then
|
55
|
+
rails="$railsapp/bin/rails"
|
56
|
+
rake="$railsapp/bin/rake"
|
57
|
+
export PATH="$railsapp/bin:$PATH"
|
58
|
+
fi
|
59
|
+
|
60
|
+
echo "Using rails=$rails, rake=$rake"
|
61
|
+
|