Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add solr monitor #121

Merged
merged 3 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ gem 'sidekiq', '>= 3.0'
gem 'spork'
gem 'sqlite3', '~> 1.3'
gem 'timecop'
gem 'webmock'

gemspec
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ The following services are currently supported:
* Sidekiq
* Resque
* Delayed Job
* Solr

## Configuration

Expand Down Expand Up @@ -312,6 +313,10 @@ Please note that `url` or `connection` can't be used at the same time.

* `queue_size`: the size (maximum) of a queue which is considered unhealthy (the default is 100).

### Solr

* `url`: the URL used to connect to your Solr instance - must be a string. You can also use `url` to explicitly configure authentication (e.g., `'https://user:[email protected]:8983/'`)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you update the example to https? 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

### Adding a Custom Provider

It's also possible to add custom health check providers suited for your needs (of course, it's highly appreciated and encouraged if you'd contribute useful providers to the project).
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails_6.1.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ gem 'sidekiq', '>= 3.0'
gem 'spork'
gem 'sqlite3', '~> 1.3'
gem 'timecop'
gem 'webmock'

gemspec path: '../'
1 change: 1 addition & 0 deletions gemfiles/rails_7.1_.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ gem 'sidekiq', '>= 3.0'
gem 'spork'
gem 'sqlite3', '~> 1.3'
gem 'timecop'
gem 'webmock'

gemspec path: '../'
2 changes: 1 addition & 1 deletion lib/health_monitor/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module HealthMonitor
class Configuration
PROVIDERS = %i[cache database delayed_job redis resque sidekiq].freeze
PROVIDERS = %i[cache database delayed_job redis resque sidekiq solr].freeze

attr_accessor :basic_auth_credentials,
:environment_variables,
Expand Down
60 changes: 60 additions & 0 deletions lib/health_monitor/providers/solr.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# frozen_string_literal: true

require 'health_monitor/providers/base'

module HealthMonitor
module Providers
class SolrException < StandardError; end

class Solr < Base
class Configuration < Base::Configuration
DEFAULT_URL = nil
attr_accessor :url

def initialize(provider)
super(provider)

@url = DEFAULT_URL
end
end

def check!
check_solr_connection!
rescue Exception => e
raise SolrException.new(e.message)
end

private

def configuration_class
::HealthMonitor::Providers::Solr::Configuration
end

def check_solr_connection!
json = JSON.parse(solr_response.body)
raise "The solr has an invalid status #{status_uri}" if json['responseHeader']['status'] != 0
end

def status_uri
@status_uri ||= begin
uri = URI(configuration.url)
uri.path = '/solr/admin/cores'
uri.query = 'action=STATUS'
uri
end
end

def solr_request
@solr_request ||= begin
req = Net::HTTP::Get.new(status_uri)
req.basic_auth(status_uri.user, status_uri.password) if status_uri.user && status_uri.password
req
end
end

def solr_response
Net::HTTP.start(status_uri.hostname, status_uri.port) { |http| http.request(solr_request) }
end
end
end
end
121 changes: 121 additions & 0 deletions spec/lib/health_monitor/providers/solr_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# frozen_string_literal: true

require 'spec_helper'

describe HealthMonitor::Providers::Solr do
subject { described_class.new }

context 'with defaults' do
it { expect(subject.configuration.name).to eq('Solr') }
it { expect(subject.configuration.url).to eq(HealthMonitor::Providers::Solr::Configuration::DEFAULT_URL) }
end

describe '#name' do
it { expect(subject.name).to eq('Solr') }
end

describe '#check!' do
let(:solr_url_config) { 'http://www.example-solr.com:8983' }

before do
subject.request = test_request
subject.configure do |config|
config.url = solr_url_config
end
Providers.stub_solr
end

context 'with a standard connection' do
it 'checks against the configured solr url' do
subject.check!
expect(Providers.stub_solr).to have_been_requested
end

it 'succesfully checks' do
expect {
subject.check!
}.not_to raise_error
end

context 'when failing' do
before do
Providers.stub_solr_failure
end

it 'fails check!' do
expect {
subject.check!
}.to raise_error(HealthMonitor::Providers::SolrException)
end

it 'checks against the configured solr url' do
expect {
subject.check!
}.to raise_error(HealthMonitor::Providers::SolrException)
expect(Providers.stub_solr_failure).to have_been_requested
end
end
end

context 'with a configured url that includes a path' do
let(:solr_url_config) { 'http://www.example-solr.com:8983/solr/blacklight-core-development' }

it 'checks against the configured solr url' do
subject.check!
expect(Providers.stub_solr).to have_been_requested
end
end

context 'with a connection with authentication' do
let(:solr_url_config) { 'http://solr:SolrRocks@localhost:8888' }

before { Providers.stub_solr_with_auth }

it 'checks against the configured solr url' do
subject.check!
expect(Providers.stub_solr_with_auth).to have_been_requested
end

it 'succesfully checks' do
expect {
subject.check!
}.not_to raise_error
end

context 'when failing' do
before do
Providers.stub_solr_failure_with_auth
end

it 'fails check!' do
expect {
subject.check!
}.to raise_error(HealthMonitor::Providers::SolrException)
end

it 'checks against the configured solr url' do
expect {
subject.check!
}.to raise_error(HealthMonitor::Providers::SolrException)
expect(Providers.stub_solr_failure_with_auth).to have_been_requested
end
end
end
end

describe '#configure' do
before do
subject.configure
end

let(:url) { 'solr://user:[email protected]:8983/' }

it 'url can be configured' do
expect {
subject.configure do |config|
config.url = url
end
}.to change { subject.configuration.url }.to(url)
end
end
end
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
require 'timecop'
require 'mock_redis'
require 'sidekiq/testing'
require 'webmock/rspec'

Dir[File.expand_path('../lib/**/*.rb', __dir__)].sort.each { |f| require f }
Dir[File.expand_path('support/**/*.rb', __dir__)].sort.each { |f| require f }
Expand Down
24 changes: 24 additions & 0 deletions spec/support/providers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,28 @@ def stub_sidekiq_over_retry_limit_failure
allow(retry_set).to receive(:select).and_return([item: { retry_count: retry_count }])
allow(Sidekiq::RetrySet).to receive(:new).and_return(retry_set)
end

def stub_solr
WebMock.stub_request(:get, 'http://www.example-solr.com:8983/solr/admin/cores?action=STATUS').to_return(
body: { responseHeader: { status: 0 } }.to_json, headers: { 'Content-Type' => 'text/json' }
)
end

def stub_solr_failure
WebMock.stub_request(:get, 'http://www.example-solr.com:8983/solr/admin/cores?action=STATUS').to_return(
body: { responseHeader: { status: 500 } }.to_json, headers: { 'Content-Type' => 'text/json' }
)
end

def stub_solr_with_auth
WebMock.stub_request(:get, 'http://localhost:8888/solr/admin/cores?action=STATUS')
.with(headers: { 'Authorization' => 'Basic c29scjpTb2xyUm9ja3M=', 'Host' => 'localhost:8888' })
.to_return(body: { responseHeader: { status: 0 } }.to_json, headers: { 'Content-Type' => 'text/json' })
end

def stub_solr_failure_with_auth
WebMock.stub_request(:get, 'http://localhost:8888/solr/admin/cores?action=STATUS')
.with(headers: { 'Authorization' => 'Basic c29scjpTb2xyUm9ja3M=', 'Host' => 'localhost:8888' })
.to_return(body: { responseHeader: { status: 500 } }.to_json, headers: { 'Content-Type' => 'text/json' })
end
end
Loading