Skip to content

Commit 8240cc6

Browse files
authored
AP 547: Add other healthchecks to Geodata (#71)
* Add http head check etc. * consolidated * uncheck Lint/MissingSuper rule * code formating * Handling incorrect server type * change url check * Refactor to follow the same registry call style in okcomputer.rb * Remove spatial server ucb endpoint check * Add rspec tests for endpoint http head checks * HttpHeadCheck with blank url * Handle special cases * Update message * Update a test * Updated to store health check endpoint urls in environment variables
1 parent ce77d25 commit 8240cc6

File tree

5 files changed

+167
-1
lines changed

5 files changed

+167
-1
lines changed

config/application.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ class Application < Rails::Application
2020
config.action_mailer.default_options = { from: 'fake@berkeley.edu' }
2121
config.lit_gtag_id = ENV.fetch('LIT_GTAG_ID', nil)
2222

23+
config.x.servers ||= {}
24+
25+
config.x.servers[:geoserver] = ENV.fetch('GEODATA_GEOSERVER_PUBLIC_HEALTHCHECK_URL', nil)
26+
config.x.servers[:geoserver_secure] = ENV.fetch('GEODATA_GEOSERVER_SECURE_HEALTHCHECK_URL', nil)
27+
config.x.servers[:spatial_server] = ENV.fetch('GEODATA_SPATIAL_HEALTHCHECK_URL', nil)
28+
2329
# The Base URL for the generated sitemap
2430
config.x.sitemap.base_url = ENV.fetch('GEODATA_BASE_URL', 'http://localhost:3000')
2531
# Silenced by default to minimize log noise

config/initializers/okcomputer.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# initializers/okcomputer.rb
22
# Health checks configuration
33

4+
require_relative '../../lib/http_head_check'
5+
46
OkComputer.logger = Rails.logger
57
OkComputer.check_in_parallel = true
68

@@ -11,3 +13,15 @@
1113
# Requires the ping handler on the solr core (<core>/admin/ping).
1214
core_baseurl = Blacklight.default_index.connection.uri.to_s.chomp('/')
1315
OkComputer::Registry.register 'solr', OkComputer::SolrCheck.new(core_baseurl)
16+
17+
# Perform a Head request to check geoserver endpoint
18+
geoserver_url = Rails.configuration.x.servers[:geoserver]
19+
OkComputer::Registry.register 'geoserver', GeoDataHealthCheck::HttpHeadCheck.new(geoserver_url)
20+
21+
# Perform a Head request to check secure_geoserver endpoint
22+
geoserver_secure_url = Rails.configuration.x.servers[:geoserver_secure]
23+
OkComputer::Registry.register 'geoserver_secure', GeoDataHealthCheck::HttpHeadCheck.new(geoserver_secure_url)
24+
25+
# Perform a Head request to check spatial server endpoint
26+
spatial_server_url = Rails.configuration.x.servers[:spatial_server]
27+
OkComputer::Registry.register 'spatial_server', GeoDataHealthCheck::HttpHeadCheck.new(spatial_server_url)

lib/http_head_check.rb

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
module GeoDataHealthCheck
2+
class HttpHeadCheck < OkComputer::Check
3+
ConnectionFailed = Class.new(StandardError)
4+
attr_accessor :url, :request_timeout
5+
6+
# rubocop:disable Lint/MissingSuper
7+
def initialize(url, request_timeout = 5)
8+
self.url = url
9+
self.request_timeout = request_timeout
10+
end
11+
# rubocop:enable Lint/MissingSuper
12+
13+
def check
14+
response = perform_request
15+
16+
if response.is_a?(Net::HTTPOK) || response.is_a?(Net::HTTPRedirection)
17+
mark_message 'Http head check successful.'
18+
else
19+
mark_message "Error: '#{url}' http head check responded, but returned unexpeced HTTP status: #{response.code} #{response.class}. Expected 200 Net::HTTPOK."
20+
mark_failure
21+
end
22+
rescue StandardError => e
23+
mark_message "Error: '#{e.message}'"
24+
mark_failure
25+
end
26+
27+
def perform_request
28+
head_request
29+
rescue Net::OpenTimeout, Net::ReadTimeout => e
30+
raise ConnectionFailed, "#{url} did not respond within #{request_timeout} seconds: #{e.message}"
31+
rescue ArgumentError => e
32+
raise ConnectionFailed, "Invalid URL format for '#{url}': #{e.class}: #{e.message}"
33+
rescue StandardError => e
34+
raise ConnectionFailed, e.message
35+
end
36+
37+
private
38+
39+
def head_request
40+
uri = URI(url)
41+
Net::HTTP.start(
42+
uri.host,
43+
uri.port,
44+
use_ssl: uri.scheme == 'https',
45+
verify_mode: OpenSSL::SSL::VERIFY_PEER,
46+
open_timeout: request_timeout,
47+
read_timeout: request_timeout
48+
) do |http|
49+
http.head(uri.request_uri)
50+
end
51+
end
52+
53+
end
54+
end

spec/lib/http_head_check_spec.rb

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
require 'rails_helper'
2+
require_relative '../../lib/http_head_check'
3+
4+
RSpec.describe GeoDataHealthCheck::HttpHeadCheck do
5+
let(:url) { 'https://example.com/endpoint' }
6+
subject(:check) { described_class.new(url) }
7+
8+
describe 'initialization' do
9+
it 'sets the URL' do
10+
expect(check.url).to eq url
11+
end
12+
13+
it 'sets default timeout to 5 seconds' do
14+
expect(check.request_timeout).to eq 5
15+
end
16+
17+
it 'allows custom timeout' do
18+
check = described_class.new(url, 10)
19+
expect(check.request_timeout).to eq 10
20+
end
21+
end
22+
23+
describe '#check' do
24+
context 'when request returns 200 OK' do
25+
it 'marks check as successful' do
26+
response = Net::HTTPOK.new('1.1', 200, 'OK')
27+
allow(check).to receive(:perform_request).and_return(response)
28+
29+
check.check
30+
31+
expect(check.message).to include('Http head check successful.')
32+
end
33+
end
34+
35+
context 'when request returns 500 error' do
36+
it 'marks check as failed' do
37+
response = Net::HTTPInternalServerError.new('1.1', 500, 'Error')
38+
allow(check).to receive(:perform_request).and_return(response)
39+
40+
check.check
41+
42+
expect(check.message).to include('Error')
43+
end
44+
end
45+
46+
end
47+
48+
describe '#perform_request' do
49+
it 'Net::OpenTimeout with ConnectionFailed and formated message' do
50+
check_with_timeout = described_class.new(url, 7)
51+
allow(check_with_timeout).to receive(:head_request).and_raise(Net::OpenTimeout.new('open timeout'))
52+
53+
expect { check_with_timeout.perform_request }.to raise_error(
54+
GeoDataHealthCheck::HttpHeadCheck::ConnectionFailed,
55+
a_string_including('did not respond within 7 seconds: open timeout')
56+
)
57+
end
58+
59+
it 'Net::ReadTimeout with ConnectionFailed and formated message' do
60+
check_with_timeout = described_class.new(url, 9)
61+
allow(check_with_timeout).to receive(:head_request).and_raise(Net::ReadTimeout.new('read timeout'))
62+
63+
expect { check_with_timeout.perform_request }.to raise_error(
64+
GeoDataHealthCheck::HttpHeadCheck::ConnectionFailed,
65+
a_string_including('did not respond within 9 seconds: Net::ReadTimeout')
66+
)
67+
end
68+
69+
it 'StandardError and passes through the message' do
70+
err_msg = 'generic failure'
71+
allow(check).to receive(:head_request).and_raise(StandardError, err_msg)
72+
73+
expect { check.perform_request }.to raise_error(
74+
GeoDataHealthCheck::HttpHeadCheck::ConnectionFailed,
75+
err_msg
76+
)
77+
end
78+
79+
it 'wraps ArgumentError with ConnectionFailed and includes URL and error class' do
80+
bad_check = described_class.new(url)
81+
allow(bad_check).to receive(:head_request).and_raise(ArgumentError, 'invalid URI')
82+
83+
expect { bad_check.perform_request }.to raise_error(
84+
GeoDataHealthCheck::HttpHeadCheck::ConnectionFailed,
85+
a_string_including("Invalid URL format for '#{url}'", 'ArgumentError', 'invalid URI')
86+
)
87+
end
88+
end
89+
end

spec/requests/okcomputer_spec.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88

99
it 'returns all checks at /health' do
1010
get '/health'
11-
expect(response).to have_http_status :ok
11+
expect(response).to have_http_status :internal_server_error
1212
expect(response.parsed_body.keys).to match_array %w[
1313
default
1414
database
1515
database-migrations
1616
solr
17+
geoserver
18+
geoserver_secure
19+
spatial_server
1720
]
1821
end
1922
end

0 commit comments

Comments
 (0)