123 lines
3.8 KiB
Ruby
123 lines
3.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'webrick'
|
|
require 'prometheus/client/rack/exporter'
|
|
|
|
module Gitlab
|
|
module Metrics
|
|
module Exporter
|
|
class BaseExporter < Daemon
|
|
CERT_REGEX = /-----BEGIN CERTIFICATE-----(?:.|\n)+?-----END CERTIFICATE-----/.freeze
|
|
|
|
attr_reader :server
|
|
|
|
# @param settings [Hash] SettingsLogic hash containing the `*_exporter` config
|
|
# @param log_enabled [Boolean] whether to log HTTP requests
|
|
# @param log_file [String] path to where the server log should be located
|
|
# @param gc_requests [Boolean] whether to run a major GC after each scraper request
|
|
def initialize(settings, log_enabled:, log_file:, gc_requests: false, **options)
|
|
super(**options)
|
|
|
|
@settings = settings
|
|
@gc_requests = gc_requests
|
|
|
|
# log_enabled does not exist for all exporters
|
|
log_sink = log_enabled ? File.join(Rails.root, 'log', log_file) : File::NULL
|
|
@logger = WEBrick::Log.new(log_sink)
|
|
@logger.time_format = "[%Y-%m-%dT%H:%M:%S.%L%z]"
|
|
end
|
|
|
|
def enabled?
|
|
settings.enabled
|
|
end
|
|
|
|
private
|
|
|
|
attr_reader :settings, :logger
|
|
|
|
def start_working
|
|
access_log = [
|
|
[logger, WEBrick::AccessLog::COMBINED_LOG_FORMAT]
|
|
]
|
|
|
|
server_config = {
|
|
Port: settings.port,
|
|
BindAddress: settings.address,
|
|
Logger: logger,
|
|
AccessLog: access_log
|
|
}
|
|
|
|
server_config.merge!(ssl_config) if settings['tls_enabled']
|
|
|
|
@server = ::WEBrick::HTTPServer.new(server_config)
|
|
server.mount '/', Rack::Handler::WEBrick, rack_app
|
|
|
|
true
|
|
rescue StandardError => e
|
|
logger.error(e)
|
|
false
|
|
end
|
|
|
|
def run_thread
|
|
server&.start
|
|
rescue IOError
|
|
# ignore forcibily closed servers
|
|
end
|
|
|
|
def stop_working
|
|
if server
|
|
# we close sockets if thread is not longer running
|
|
# this happens, when the process forks
|
|
if thread.alive?
|
|
server.shutdown
|
|
else
|
|
server.listeners.each(&:close)
|
|
end
|
|
end
|
|
|
|
@server = nil
|
|
end
|
|
|
|
def rack_app
|
|
pid = thread_name
|
|
gc_requests = @gc_requests
|
|
|
|
Rack::Builder.app do
|
|
use Rack::Deflater
|
|
use Gitlab::Metrics::Exporter::MetricsMiddleware, pid
|
|
use Gitlab::Metrics::Exporter::GcRequestMiddleware if gc_requests
|
|
use ::Prometheus::Client::Rack::Exporter if ::Gitlab::Metrics.metrics_folder_present?
|
|
run -> (env) { [404, {}, ['']] }
|
|
end
|
|
end
|
|
|
|
def ssl_config
|
|
# This monkey-patches WEBrick::GenericServer, so never require this unless TLS is enabled.
|
|
require 'webrick/ssl'
|
|
|
|
certs = load_ca_certs_bundle(File.binread(settings['tls_cert_path']))
|
|
|
|
{
|
|
SSLEnable: true,
|
|
SSLCertificate: certs.shift,
|
|
SSLPrivateKey: OpenSSL::PKey.read(File.binread(settings['tls_key_path'])),
|
|
# SSLStartImmediately is true by default according to the docs, but when WEBrick creates the
|
|
# SSLServer internally, the switch was always nil for some reason. Setting this explicitly fixes this.
|
|
SSLStartImmediately: true,
|
|
SSLExtraChainCert: certs
|
|
}
|
|
end
|
|
|
|
# In Ruby OpenSSL v3.0.0, this can be replaced by OpenSSL::X509::Certificate.load
|
|
# https://github.com/ruby/openssl/issues/254
|
|
def load_ca_certs_bundle(ca_certs_string)
|
|
return [] unless ca_certs_string
|
|
|
|
ca_certs_string.scan(CERT_REGEX).map do |ca_cert_string|
|
|
OpenSSL::X509::Certificate.new(ca_cert_string)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|