health_check_rb 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/linters.yml +27 -0
  3. data/.github/workflows/tests.yml +50 -0
  4. data/.gitignore +33 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +110 -0
  7. data/CHANGELOG +122 -0
  8. data/Gemfile +23 -0
  9. data/MIT-LICENSE +23 -0
  10. data/README.md +392 -0
  11. data/Rakefile +27 -0
  12. data/config/routes.rb +7 -0
  13. data/health_check_rb.gemspec +26 -0
  14. data/init.rb +3 -0
  15. data/lib/health_check_rb/base_health_check.rb +7 -0
  16. data/lib/health_check_rb/check/elasticsearch.rb +18 -0
  17. data/lib/health_check_rb/check/rabbitmq.rb +19 -0
  18. data/lib/health_check_rb/check/redis.rb +30 -0
  19. data/lib/health_check_rb/check/resque.rb +18 -0
  20. data/lib/health_check_rb/check/s3.rb +62 -0
  21. data/lib/health_check_rb/check/sidekiq.rb +20 -0
  22. data/lib/health_check_rb/health_check_controller.rb +80 -0
  23. data/lib/health_check_rb/health_check_routes.rb +18 -0
  24. data/lib/health_check_rb/middleware_health_check.rb +108 -0
  25. data/lib/health_check_rb/utils.rb +194 -0
  26. data/lib/health_check_rb/version.rb +5 -0
  27. data/lib/health_check_rb.rb +129 -0
  28. data/spec/dummy/Rakefile +8 -0
  29. data/spec/dummy/app/assets/config/manifest.js +1 -0
  30. data/spec/dummy/app/controllers/example_controller.rb +7 -0
  31. data/spec/dummy/config/database.yml +10 -0
  32. data/spec/dummy/config/initializers/health_check.rb +18 -0
  33. data/spec/dummy/config/initializers/middleware.rb +3 -0
  34. data/spec/dummy/config/routes.rb +5 -0
  35. data/spec/dummy/db/migrate/.keep +0 -0
  36. data/spec/dummy/fake_app.rb +18 -0
  37. data/spec/dummy/tmp/.keep +0 -0
  38. data/spec/fixtures/migrate/9_create_countries.rb +9 -0
  39. data/spec/health_check_rb_spec.rb +300 -0
  40. data/spec/spec_helper.rb +36 -0
  41. metadata +101 -0
@@ -0,0 +1,300 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe HealthCheckRb, type: :request do
6
+ context 'with /custom_route_prefix' do
7
+ it 'works with smtp server and valid custom_check' do
8
+ enable_custom_check do
9
+ mock_smtp_server do
10
+ get '/custom_route_prefix'
11
+ expect(response).to be_ok
12
+ end
13
+ end
14
+ end
15
+
16
+ it 'fails with no smtp server and valid custom_check' do
17
+ enable_custom_check do
18
+ get '/custom_route_prefix'
19
+ expect(response.status).to eq(550)
20
+ expect(response.body).to include 'health_check failed'
21
+ end
22
+ end
23
+
24
+ it 'fails with smtp server and invalid custom_check' do
25
+ mock_smtp_server do
26
+ get '/custom_route_prefix'
27
+ expect(response.status).to eq(550)
28
+ expect(response.body).to include 'health_check failed'
29
+ end
30
+ end
31
+ end
32
+
33
+ context 'with /custom_route_prefix/all' do
34
+ it 'works with smtp server and valid custom_check' do
35
+ enable_custom_check do
36
+ mock_smtp_server do
37
+ get '/custom_route_prefix/all'
38
+ expect(response).to be_ok
39
+ end
40
+ end
41
+ end
42
+
43
+ it 'fails with no smtp server and valid custom_check' do
44
+ enable_custom_check do
45
+ get '/custom_route_prefix/all'
46
+ expect(response.status).to eq(550)
47
+ expect(response.body).to include 'health_check failed'
48
+ end
49
+ end
50
+
51
+ it 'fails with smtp server and invalid custom_check' do
52
+ mock_smtp_server do
53
+ get '/custom_route_prefix/all'
54
+ expect(response.status).to eq(550)
55
+ expect(response.body).to include 'health_check failed'
56
+ end
57
+ end
58
+ end
59
+
60
+ context 'with /custom_route_prefix/migration' do
61
+ before { reconnect_database }
62
+
63
+ after do
64
+ Dir.glob('spec/dummy/db/migrate/*').each do |f|
65
+ FileUtils.rm f
66
+ end
67
+ FileUtils.rm 'spec/dummy/db/schema.rb' if File.exist? 'spec/dummy/db/schema.rb'
68
+ FileUtils.cd FakeApp.config.root do
69
+ ActiveRecord::Tasks::DatabaseTasks.drop_current
70
+ end
71
+ end
72
+
73
+ it 'works with no migration files' do
74
+ get '/custom_route_prefix/migration'
75
+ expect(response).to be_ok
76
+ end
77
+
78
+ it 'fails with pending migration files' do
79
+ FileUtils.cp 'spec/fixtures/migrate/9_create_countries.rb', 'spec/dummy/db/migrate/9_create_countries.rb'
80
+ FileUtils.cd FakeApp.config.root do
81
+ get '/custom_route_prefix/migration'
82
+ end
83
+ expect(response.status).to eq(550)
84
+ end
85
+
86
+ it 'works with applied migration files' do
87
+ FileUtils.cp 'spec/fixtures/migrate/9_create_countries.rb', 'spec/dummy/db/migrate/9_create_countries.rb'
88
+ FileUtils.cd FakeApp.config.root do
89
+ db_migrate
90
+ get '/custom_route_prefix/migration'
91
+ end
92
+ expect(response).to be_ok
93
+ end
94
+ end
95
+
96
+ describe '/custom_route_prefix/database' do
97
+ after do
98
+ Dir.glob('spec/dummy/db/migrate/*').each do |f|
99
+ FileUtils.rm f
100
+ end
101
+ FileUtils.rm 'spec/dummy/db/schema.rb' if File.exist? 'spec/dummy/db/schema.rb'
102
+ FileUtils.cd FakeApp.config.root do
103
+ ActiveRecord::Tasks::DatabaseTasks.drop_current
104
+ end
105
+ end
106
+
107
+ it 'works with no database' do
108
+ get '/custom_route_prefix/database'
109
+ expect(response).to be_ok
110
+ end
111
+
112
+ it 'works with valid database' do
113
+ FileUtils.cp 'spec/fixtures/migrate/9_create_countries.rb', 'spec/dummy/db/migrate/9_create_countries.rb'
114
+ FileUtils.cd FakeApp.config.root do
115
+ db_migrate
116
+ get '/custom_route_prefix/database'
117
+ end
118
+ expect(response).to be_ok
119
+ end
120
+
121
+ it 'fails with invalid database' do
122
+ disconnect_database
123
+ Rails.root.join('db/test.sqlite3').write('invalid')
124
+ get '/custom_route_prefix/database'
125
+ expect(response.status).to eq(550)
126
+ expect(response.body).to include 'health_check failed'
127
+ end
128
+ end
129
+
130
+ describe '/custom_route_prefix/email' do
131
+ it 'works with smtp server' do
132
+ mock_smtp_server do
133
+ get '/custom_route_prefix/email'
134
+ expect(response).to be_ok
135
+ end
136
+ end
137
+
138
+ it 'fails with no smtp server' do
139
+ get '/custom_route_prefix/email'
140
+ expect(response.status).to eq(550)
141
+ expect(response.body).to include 'health_check failed'
142
+ end
143
+ end
144
+
145
+ describe '/custom_route_prefix/pass (a custom check does nothing)' do
146
+ it 'works if another custom check is invalid' do
147
+ get '/custom_route_prefix/pass'
148
+ expect(response).to be_ok
149
+ end
150
+
151
+ it 'works if another custom check is valid' do
152
+ enable_custom_check do
153
+ get '/custom_route_prefix/pass'
154
+ expect(response).to be_ok
155
+ end
156
+ end
157
+ end
158
+
159
+ describe '/heath_check/custom' do
160
+ it 'works with valid custom check' do
161
+ enable_custom_check do
162
+ get '/custom_route_prefix/custom'
163
+ end
164
+ expect(response).to be_ok
165
+ end
166
+
167
+ it 'fails with invalid custom check' do
168
+ get '/custom_route_prefix/custom'
169
+ expect(response.status).to eq(550)
170
+ expect(response.body).to include 'health_check failed'
171
+ end
172
+
173
+ context 'with specified format' do
174
+ it 'returns plain text if client requests html format' do
175
+ enable_custom_check do
176
+ get '/custom_route_prefix/custom.html'
177
+ end
178
+ expect(response).to be_ok
179
+ expect(response.content_type).to include('text/plain')
180
+ end
181
+
182
+ it 'returns json if client requests json format' do
183
+ enable_custom_check do
184
+ get '/custom_route_prefix/custom.json'
185
+ end
186
+ expect(response).to be_ok
187
+ expect(response.content_type).to include('application/json')
188
+ expect(response.parsed_body).to include('healthy' => true, 'message' => 'custom_success_message')
189
+ end
190
+
191
+ it 'returns json if client requests json format and custom check is invalid' do
192
+ get '/custom_route_prefix/custom.json'
193
+ expect(response.status).to eq(555) # config.http_status_for_error_object = 555
194
+ expect(response.content_type).to include('application/json')
195
+ expect(response.parsed_body).to include('healthy' => false)
196
+ end
197
+
198
+ it 'returns xml if client requests xml format' do
199
+ enable_custom_check do
200
+ get '/custom_route_prefix/custom.xml'
201
+ end
202
+ expect(response).to be_ok
203
+ expect(response.content_type).to include('application/xml')
204
+ expect(response.body).to include('<healthy type="boolean">true</healthy>')
205
+ expect(response.body).to include('<message>custom_success_message</message>')
206
+ end
207
+
208
+ it 'returns xml if client requests xml format and custom check is invalid' do
209
+ get '/custom_route_prefix/custom.xml'
210
+ expect(response.status).to eq(555) # config.http_status_for_error_object = 555
211
+ expect(response.content_type).to include('application/xml')
212
+ expect(response.body).to include('<healthy type="boolean">false</healthy>')
213
+ end
214
+ end
215
+ end
216
+
217
+ describe '/custom_route_prefix/middleware' do
218
+ if ENV['MIDDLEWARE'] == 'true'
219
+ context 'when using middleware' do
220
+ it 'works with valid custom check' do
221
+ enable_custom_check do
222
+ get '/custom_route_prefix/middleware'
223
+ end
224
+ expect(response).to be_ok
225
+ end
226
+ end
227
+ end
228
+ if ENV['MIDDLEWARE'] != 'true'
229
+ context 'when not using middleware' do
230
+ it 'fails with invalid custom check' do
231
+ get '/custom_route_prefix/middleware'
232
+ expect(response.status).to eq(550)
233
+ expect(response.body).to include 'health_check failed'
234
+ end
235
+ end
236
+ end
237
+ end
238
+
239
+ context 'with whitelisted ip' do
240
+ after { HealthCheckRb.origin_ip_whitelist.clear }
241
+
242
+ it 'works with access from valid ip address' do
243
+ HealthCheckRb.origin_ip_whitelist << '127.0.0.1'
244
+ get '/custom_route_prefix/site'
245
+ expect(response).to be_ok
246
+ end
247
+
248
+ it 'fails with access from invalid ip address' do
249
+ HealthCheckRb.origin_ip_whitelist << '123.123.123.123'
250
+ get '/custom_route_prefix/site'
251
+ expect(response.status).to eq(403)
252
+ end
253
+
254
+ it 'does not affect paths other than health_check' do
255
+ HealthCheckRb.origin_ip_whitelist << '123.123.123.123'
256
+ get '/example'
257
+ expect(response).to be_ok
258
+ end
259
+ end
260
+
261
+ context 'with basic auth' do
262
+ before do
263
+ HealthCheckRb.basic_auth_username = 'username'
264
+ HealthCheckRb.basic_auth_password = 'password'
265
+ end
266
+
267
+ after do
268
+ HealthCheckRb.basic_auth_username = nil
269
+ HealthCheckRb.basic_auth_password = nil
270
+ end
271
+
272
+ it 'works with valid credentials' do
273
+ get '/custom_route_prefix/site',
274
+ headers: { 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials('username', 'password') }
275
+ expect(response).to be_ok
276
+ end
277
+
278
+ it 'fails with wrong password' do
279
+ get '/custom_route_prefix/site',
280
+ headers: { 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials('username', 'wrong_password') }
281
+ expect(response.status).to eq(401)
282
+ end
283
+
284
+ it 'fails with wrong username' do
285
+ get '/custom_route_prefix/site',
286
+ headers: { 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials('wrong_username', 'password') }
287
+ expect(response.status).to eq(401)
288
+ end
289
+
290
+ it 'fails with no credentials' do
291
+ get '/custom_route_prefix/site'
292
+ expect(response.status).to eq(401)
293
+ end
294
+
295
+ it 'does not affect paths other than health_check' do
296
+ get '/example'
297
+ expect(response).to be_ok
298
+ end
299
+ end
300
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'dummy/fake_app'
4
+ require 'rspec/rails'
5
+ require 'smtp_mock/test_framework/rspec'
6
+
7
+ RSpec.configure do |config|
8
+ config.include SmtpMock::TestFramework::RSpec::Helper
9
+ end
10
+
11
+ # see https://github.com/mocktools/ruby-smtp-mock
12
+ def mock_smtp_server(&)
13
+ smtp_mock_server port: 3555 do
14
+ sleep 1
15
+ yield
16
+ end
17
+ end
18
+
19
+ def enable_custom_check(&)
20
+ File.write CUSTOM_CHECK_FILE_PATH, 'hello'
21
+ yield
22
+ ensure
23
+ FileUtils.rm CUSTOM_CHECK_FILE_PATH if File.exist? CUSTOM_CHECK_FILE_PATH
24
+ end
25
+
26
+ def disconnect_database
27
+ ActiveRecord::Tasks::DatabaseTasks.migration_connection.disconnect!
28
+ end
29
+
30
+ def reconnect_database
31
+ ActiveRecord::Tasks::DatabaseTasks.migration_connection.reconnect!
32
+ end
33
+
34
+ def db_migrate
35
+ system 'RAILS_ENV=test bundle exec rake db:migrate'
36
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: health_check_rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 4.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Ian Heggie
8
+ - Alexander Meindl
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2025-02-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: railties
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '5.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '5.0'
28
+ description: " \tSimple health check of Rails app for uptime monitoring with Pingdom,
29
+ NewRelic, EngineYard etc.\n"
30
+ email:
31
+ - ian@heggie.biz
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files:
35
+ - README.md
36
+ files:
37
+ - ".github/workflows/linters.yml"
38
+ - ".github/workflows/tests.yml"
39
+ - ".gitignore"
40
+ - ".rspec"
41
+ - ".rubocop.yml"
42
+ - CHANGELOG
43
+ - Gemfile
44
+ - MIT-LICENSE
45
+ - README.md
46
+ - Rakefile
47
+ - config/routes.rb
48
+ - health_check_rb.gemspec
49
+ - init.rb
50
+ - lib/health_check_rb.rb
51
+ - lib/health_check_rb/base_health_check.rb
52
+ - lib/health_check_rb/check/elasticsearch.rb
53
+ - lib/health_check_rb/check/rabbitmq.rb
54
+ - lib/health_check_rb/check/redis.rb
55
+ - lib/health_check_rb/check/resque.rb
56
+ - lib/health_check_rb/check/s3.rb
57
+ - lib/health_check_rb/check/sidekiq.rb
58
+ - lib/health_check_rb/health_check_controller.rb
59
+ - lib/health_check_rb/health_check_routes.rb
60
+ - lib/health_check_rb/middleware_health_check.rb
61
+ - lib/health_check_rb/utils.rb
62
+ - lib/health_check_rb/version.rb
63
+ - spec/dummy/Rakefile
64
+ - spec/dummy/app/assets/config/manifest.js
65
+ - spec/dummy/app/controllers/example_controller.rb
66
+ - spec/dummy/config/database.yml
67
+ - spec/dummy/config/initializers/health_check.rb
68
+ - spec/dummy/config/initializers/middleware.rb
69
+ - spec/dummy/config/routes.rb
70
+ - spec/dummy/db/migrate/.keep
71
+ - spec/dummy/fake_app.rb
72
+ - spec/dummy/tmp/.keep
73
+ - spec/fixtures/migrate/9_create_countries.rb
74
+ - spec/health_check_rb_spec.rb
75
+ - spec/spec_helper.rb
76
+ homepage: https://github.com/alphanodes/health_check_rb
77
+ licenses:
78
+ - MIT
79
+ metadata:
80
+ rubygems_mfa_required: 'true'
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '3.1'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubygems_version: 3.4.19
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: Simple health check of Rails app for uptime monitoring with Pingdom, NewRelic,
100
+ EngineYard etc.
101
+ test_files: []