diff options
Diffstat (limited to 'spec/lib/gitlab/metrics/exporter')
6 files changed, 178 insertions, 67 deletions
diff --git a/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb b/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb index 9cd1ef4094e..c7afc02f0af 100644 --- a/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb +++ b/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb @@ -4,13 +4,8 @@ require 'spec_helper' RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do let(:settings) { double('settings') } - let(:exporter) { described_class.new(settings) } - let(:log_filename) { File.join(Rails.root, 'log', 'sidekiq_exporter.log') } - - before do - allow_any_instance_of(described_class).to receive(:log_filename).and_return(log_filename) - allow_any_instance_of(described_class).to receive(:settings).and_return(settings) - end + let(:log_enabled) { false } + let(:exporter) { described_class.new(settings, log_enabled: log_enabled, log_file: 'test_exporter.log') } describe 'when exporter is enabled' do before do @@ -61,6 +56,38 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do exporter.start.join end + + context 'logging enabled' do + let(:log_enabled) { true } + let(:logger) { instance_double(WEBrick::Log) } + + before do + allow(logger).to receive(:time_format=) + allow(logger).to receive(:info) + end + + it 'configures a WEBrick logger with the given file' do + expect(WEBrick::Log).to receive(:new).with(end_with('test_exporter.log')).and_return(logger) + + exporter + end + + it 'logs any errors during startup' do + expect(::WEBrick::Log).to receive(:new).and_return(logger) + expect(::WEBrick::HTTPServer).to receive(:new).and_raise 'fail' + expect(logger).to receive(:error) + + exporter.start + end + end + + context 'logging disabled' do + it 'configures a WEBrick logger with the null device' do + expect(WEBrick::Log).to receive(:new).with(File::NULL).and_call_original + + exporter + end + end end describe 'when thread is not alive' do @@ -111,6 +138,18 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do describe 'request handling' do using RSpec::Parameterized::TableSyntax + let(:fake_collector) do + Class.new do + def initialize(app, ...) + @app = app + end + + def call(env) + @app.call(env) + end + end + end + where(:method_class, :path, :http_status) do Net::HTTP::Get | '/metrics' | 200 Net::HTTP::Get | '/liveness' | 200 @@ -123,6 +162,8 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do allow(settings).to receive(:port).and_return(0) allow(settings).to receive(:address).and_return('127.0.0.1') + stub_const('Gitlab::Metrics::Exporter::MetricsMiddleware', fake_collector) + # We want to wrap original method # and run handling of requests # in separate thread @@ -134,8 +175,6 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do # is raised as we close listeners end end - - exporter.start.join end after do @@ -146,12 +185,25 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do let(:config) { exporter.server.config } let(:request) { method_class.new(path) } - it 'responds with proper http_status' do + subject(:response) do http = Net::HTTP.new(config[:BindAddress], config[:Port]) - response = http.request(request) + http.request(request) + end + + it 'responds with proper http_status' do + exporter.start.join expect(response.code).to eq(http_status.to_s) end + + it 'collects request metrics' do + expect_next_instance_of(fake_collector) do |instance| + expect(instance).to receive(:call).and_call_original + end + + exporter.start.join + response + end end end diff --git a/spec/lib/gitlab/metrics/exporter/gc_request_middleware_spec.rb b/spec/lib/gitlab/metrics/exporter/gc_request_middleware_spec.rb new file mode 100644 index 00000000000..0c70a5de701 --- /dev/null +++ b/spec/lib/gitlab/metrics/exporter/gc_request_middleware_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +RSpec.describe Gitlab::Metrics::Exporter::GcRequestMiddleware do + let(:app) { double(:app) } + let(:env) { {} } + + subject(:middleware) { described_class.new(app) } + + describe '#call' do + it 'runs a major GC after the next middleware is called' do + expect(app).to receive(:call).with(env).ordered.and_return([200, {}, []]) + expect(GC).to receive(:start).ordered + + response = middleware.call(env) + + expect(response).to eq([200, {}, []]) + end + end +end diff --git a/spec/lib/gitlab/metrics/exporter/health_checks_middleware_spec.rb b/spec/lib/gitlab/metrics/exporter/health_checks_middleware_spec.rb new file mode 100644 index 00000000000..9ee46a45e7a --- /dev/null +++ b/spec/lib/gitlab/metrics/exporter/health_checks_middleware_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +RSpec.describe Gitlab::Metrics::Exporter::HealthChecksMiddleware do + let(:app) { double(:app) } + let(:env) { { 'PATH_INFO' => path } } + + let(:readiness_probe) { double(:readiness_probe) } + let(:liveness_probe) { double(:liveness_probe) } + let(:probe_result) { Gitlab::HealthChecks::Probes::Status.new(200, { status: 'ok' }) } + + subject(:middleware) { described_class.new(app, readiness_probe, liveness_probe) } + + describe '#call' do + context 'handling /readiness requests' do + let(:path) { '/readiness' } + + it 'handles the request' do + expect(readiness_probe).to receive(:execute).and_return(probe_result) + + response = middleware.call(env) + + expect(response).to eq([200, { 'Content-Type' => 'application/json; charset=utf-8' }, ['{"status":"ok"}']]) + end + end + + context 'handling /liveness requests' do + let(:path) { '/liveness' } + + it 'handles the request' do + expect(liveness_probe).to receive(:execute).and_return(probe_result) + + response = middleware.call(env) + + expect(response).to eq([200, { 'Content-Type' => 'application/json; charset=utf-8' }, ['{"status":"ok"}']]) + end + end + + context 'handling other requests' do + let(:path) { '/other_path' } + + it 'forwards them to the next middleware' do + expect(app).to receive(:call).with(env).and_return([201, {}, []]) + + response = middleware.call(env) + + expect(response).to eq([201, {}, []]) + end + end + end +end diff --git a/spec/lib/gitlab/metrics/exporter/metrics_middleware_spec.rb b/spec/lib/gitlab/metrics/exporter/metrics_middleware_spec.rb new file mode 100644 index 00000000000..ac5721f5974 --- /dev/null +++ b/spec/lib/gitlab/metrics/exporter/metrics_middleware_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Metrics::Exporter::MetricsMiddleware do + let(:app) { double(:app) } + let(:pid) { 'fake_exporter' } + let(:env) { { 'PATH_INFO' => '/path', 'REQUEST_METHOD' => 'GET' } } + + subject(:middleware) { described_class.new(app, pid) } + + def metric(name, method, path, status) + metric = ::Prometheus::Client.registry.get(name) + return unless metric + + values = metric.values.transform_keys { |k| k.slice(:method, :path, :pid, :code) } + values[{ method: method, path: path, pid: pid, code: status.to_s }]&.get + end + + before do + expect(app).to receive(:call).with(env).and_return([200, {}, []]) + end + + describe '#call', :prometheus do + it 'records a total requests metric' do + response = middleware.call(env) + + expect(response).to eq([200, {}, []]) + expect(metric(:exporter_http_requests_total, 'get', '/path', 200)).to eq(1.0) + end + + it 'records a request duration histogram' do + response = middleware.call(env) + + expect(response).to eq([200, {}, []]) + expect(metric(:exporter_http_request_duration_seconds, 'get', '/path', 200)).to be_a(Hash) + end + end +end diff --git a/spec/lib/gitlab/metrics/exporter/sidekiq_exporter_spec.rb b/spec/lib/gitlab/metrics/exporter/sidekiq_exporter_spec.rb deleted file mode 100644 index 75bc3ba9626..00000000000 --- a/spec/lib/gitlab/metrics/exporter/sidekiq_exporter_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::Metrics::Exporter::SidekiqExporter do - let(:exporter) { described_class.new(Settings.monitoring.sidekiq_exporter) } - - after do - exporter.stop - end - - context 'with valid config' do - before do - stub_config( - monitoring: { - sidekiq_exporter: { - enabled: true, - log_enabled: false, - port: 0, - address: '127.0.0.1' - } - } - ) - end - - it 'does start thread' do - expect(exporter.start).not_to be_nil - end - - it 'does not enable logging by default' do - expect(exporter.log_filename).to eq(File::NULL) - end - end - - context 'with logging enabled' do - before do - stub_config( - monitoring: { - sidekiq_exporter: { - enabled: true, - log_enabled: true, - port: 0, - address: '127.0.0.1' - } - } - ) - end - - it 'returns a valid log filename' do - expect(exporter.log_filename).to end_with('sidekiq_exporter.log') - end - end -end diff --git a/spec/lib/gitlab/metrics/exporter/web_exporter_spec.rb b/spec/lib/gitlab/metrics/exporter/web_exporter_spec.rb index 9deaecbf41b..0531bccf4b4 100644 --- a/spec/lib/gitlab/metrics/exporter/web_exporter_spec.rb +++ b/spec/lib/gitlab/metrics/exporter/web_exporter_spec.rb @@ -24,14 +24,14 @@ RSpec.describe Gitlab::Metrics::Exporter::WebExporter do exporter.stop end - context 'when running server' do + context 'when running server', :prometheus do it 'readiness probe returns succesful status' do expect(readiness_probe.http_status).to eq(200) expect(readiness_probe.json).to include(status: 'ok') expect(readiness_probe.json).to include('web_exporter' => [{ 'status': 'ok' }]) end - it 'initializes request metrics', :prometheus do + it 'initializes request metrics' do expect(Gitlab::Metrics::RailsSlis).to receive(:initialize_request_slis_if_needed!).and_call_original http = Net::HTTP.new(exporter.server.config[:BindAddress], exporter.server.config[:Port]) @@ -42,7 +42,7 @@ RSpec.describe Gitlab::Metrics::Exporter::WebExporter do end describe '#mark_as_not_running!' do - it 'readiness probe returns a failure status' do + it 'readiness probe returns a failure status', :prometheus do exporter.mark_as_not_running! expect(readiness_probe.http_status).to eq(503) |