rails_health_check 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|