diff --git a/Gemfile b/Gemfile index 3c28000..fefaf00 100644 --- a/Gemfile +++ b/Gemfile @@ -20,6 +20,7 @@ gem 'importmap-rails' gem 'jbuilder' gem 'jquery-rails' gem 'mysql2' +gem 'okcomputer' gem 'puma', '~> 5.0' gem 'rails', '~> 7.0.3' gem 'redis', '~> 4.0' @@ -44,6 +45,7 @@ group :test do gem 'rspec-rails' gem 'selenium-webdriver' gem 'webdrivers' + gem 'webmock' end group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index acddf97..334c302 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -117,6 +117,8 @@ GEM coderay (1.1.3) colorize (0.8.1) concurrent-ruby (1.1.10) + crack (0.4.5) + rexml crass (1.0.6) deprecation (1.1.0) activesupport @@ -242,6 +244,7 @@ GEM builder (>= 3.1.0) faraday (< 3) faraday-follow_redirects (>= 0.3.0, < 2) + okcomputer (1.18.4) orm_adapter (0.5.0) ostruct (0.5.5) parallel (1.22.1) @@ -419,6 +422,10 @@ GEM nokogiri (~> 1.6) rubyzip (>= 1.3.0) selenium-webdriver (~> 4.0) + webmock (3.14.0) + addressable (>= 2.8.0) + crack (>= 0.3.2) + hashdiff (>= 0.4.0, < 2.0.0) websocket (1.2.9) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) @@ -451,6 +458,7 @@ DEPENDENCIES jquery-rails mysql2 niftany (>= 0.10) + okcomputer pry-byebug puma (~> 5.0) rails (~> 7.0.3) @@ -469,6 +477,7 @@ DEPENDENCIES tzinfo-data web-console webdrivers + webmock RUBY VERSION ruby 3.1.2p20 diff --git a/config/application.rb b/config/application.rb index 5c6ad29..40adb2b 100644 --- a/config/application.rb +++ b/config/application.rb @@ -12,6 +12,7 @@ module EtdaExplore class Application < Rails::Application require 'etda_explore/solr_config' require 'overrides/resumption_token' + require 'healthchecks' config.solr = EtdaExplore::SolrConfig.new diff --git a/config/initializers/okcomputer.rb b/config/initializers/okcomputer.rb new file mode 100644 index 0000000..1905a16 --- /dev/null +++ b/config/initializers/okcomputer.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +OkComputer.mount_at = false + +OkComputer::Registry.register( + 'version', + HealthChecks::VersionCheck.new +) + +OkComputer::Registry.register( + 'solr', + HealthChecks::SolrCheck.new +) + +OkComputer.make_optional %w(version) diff --git a/config/routes.rb b/config/routes.rb index fcd429c..6c61561 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,6 +6,8 @@ concern :searchable, Blacklight::Routes::Searchable.new concern :oai_provider, BlacklightOaiProvider::Routes.new + mount OkComputer::Engine, at: '/health' + authenticate :user do get '/login', to: 'application#login', as: :login end diff --git a/lib/healthchecks.rb b/lib/healthchecks.rb new file mode 100644 index 0000000..70acd44 --- /dev/null +++ b/lib/healthchecks.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module Healthchecks + require 'healthchecks/version_check' + require 'healthchecks/solr_check' +end diff --git a/lib/healthchecks/solr_check.rb b/lib/healthchecks/solr_check.rb new file mode 100644 index 0000000..1d1098a --- /dev/null +++ b/lib/healthchecks/solr_check.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module HealthChecks + class SolrCheck < OkComputer::Check + def check + solr = RSolr.connect(url: Rails.configuration.solr.query_url) + resp = solr.get('admin/ping') + status = resp&.dig('status') + zk_connected = resp&.dig('responseHeader')&.dig('zkConnected') + @message = Hash.new + @message['zkConnected'] = zk_connected + @message['status'] = status + mark_message(@message) + mark_failure unless status == 'OK' + mark_failure unless zk_connected == true + end + end +end diff --git a/lib/healthchecks/version_check.rb b/lib/healthchecks/version_check.rb new file mode 100644 index 0000000..dcb49f5 --- /dev/null +++ b/lib/healthchecks/version_check.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module HealthChecks + class VersionCheck < OkComputer::Check + def check + version = ENV.fetch('APP_VERSION', 'unknown') + mark_message version.to_s + end + end +end diff --git a/spec/lib/healthchecks/solr_check_spec.rb b/spec/lib/healthchecks/solr_check_spec.rb new file mode 100644 index 0000000..4a86b2b --- /dev/null +++ b/spec/lib/healthchecks/solr_check_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe HealthChecks::SolrCheck do + describe '#check' do + before(:all) do + WebMock.disable_net_connect! + end + + after(:all) do + WebMock.enable_net_connect! + end + + context 'when we can connect to solr' do + before do + stub_request(:get, /admin\/ping/) + .to_return(status: 200, body: { + responseHeader: { + zkConnected: true + }, + status: 'OK' + }.to_json) + end + + it 'returns no failure' do + hc = described_class.new + hc.check + expect(hc.failure_occurred).to be_nil + end + end + + context 'when zk is not connected' do + before do + stub_request(:get, /admin\/ping/) + .to_return(status: 200, body: { + responseHeader: { + zkConnected: false + }, + status: 'OK' + }.to_json) + end + + it 'returns a failure' do + hc = described_class.new + hc.check + expect(hc.failure_occurred).to be true + end + end + end +end diff --git a/spec/lib/healthchecks/version_check_spec.rb b/spec/lib/healthchecks/version_check_spec.rb new file mode 100644 index 0000000..19a8358 --- /dev/null +++ b/spec/lib/healthchecks/version_check_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe HealthChecks::VersionCheck do + describe '#check' do + context 'when version' do + before do + ENV['APP_VERSION'] = '3' + end + + it 'returns no failure' do + hc = described_class.new + hc.check + expect(hc.failure_occurred).to be_nil + end + + it 'writes a message' do + hc = described_class.new + hc.check + expect(hc.message).to eq('3') + end + end + + context 'when no version' do + before do + ENV['APP_VERSION'] = nil + end + + it 'writes unknown' do + hc = described_class.new + hc.check + expect(hc.message).to eq('unknown') + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 74a1603..ba54694 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true require 'simplecov' +require 'webmock/rspec' SimpleCov.start +WebMock.allow_net_connect! # This file was generated by the `rails generate rspec:install` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.