class Riemann::Tools::HttpCheck

Constants

REQUIRED_RUBY_VERSION

Public Class Methods

new() click to toggle source
Calls superclass method
# File lib/riemann/tools/http_check.rb, line 34
def initialize
  @resolve_queue = Queue.new
  @work_queue = Queue.new

  opts[:resolvers].times do
    Thread.new do
      loop do
        uri = @resolve_queue.pop
        host = uri.host

        addresses = Resolv::DNS.new.getaddresses(host)
        if addresses.empty?
          host = host[1...-1] if host[0] == '[' && host[-1] == ']'
          begin
            addresses << IPAddr.new(host)
          rescue IPAddr::InvalidAddressError
            # Ignore
          end
        end

        @work_queue.push([uri, addresses])
      end
    end
  end

  opts[:workers].times do
    Thread.new do
      loop do
        uri, addresses = @work_queue.pop
        test_uri_addresses(uri, addresses)
      end
    end
  end

  super
end

Public Instance Methods

endpoint_name(address, port) click to toggle source
# File lib/riemann/tools/http_check.rb, line 256
def endpoint_name(address, port)
  if address.ipv6?
    "[#{address}]:#{port}"
  else
    "#{address}:#{port}"
  end
end
endpoint_report(http, uri, service) click to toggle source
# File lib/riemann/tools/http_check.rb, line 233
def endpoint_report(http, uri, service)
  {
    service: endpoint_service(http, uri, service),
    hostname: uri.host,
    address: http.ipaddr,
    port: uri.port,
  }
end
endpoint_service(http, uri, service) click to toggle source
# File lib/riemann/tools/http_check.rb, line 242
def endpoint_service(http, uri, service)
  "get #{redact_uri(uri)} #{endpoint_name(IPAddr.new(http.ipaddr), http.port)} #{service}"
end
latency_state(name, latency) click to toggle source
# File lib/riemann/tools/http_check.rb, line 218
def latency_state(name, latency)
  critical_threshold = opts["#{name}_latency_critical".to_sym]
  warning_threshold = opts["#{name}_latency_warning".to_sym]

  return if critical_threshold.zero? || warning_threshold.zero?

  if latency.nil? || latency > critical_threshold
    'critical'
  elsif latency > warning_threshold
    'warning'
  else
    'ok'
  end
end
redact_uri(uri) click to toggle source
# File lib/riemann/tools/http_check.rb, line 250
def redact_uri(uri)
  reported_uri = uri.dup
  reported_uri.password = '**redacted**' if reported_uri.password
  reported_uri
end
report_http_endpoint_latency(http, uri, latency, start, stop) click to toggle source
# File lib/riemann/tools/http_check.rb, line 198
def report_http_endpoint_latency(http, uri, latency, start, stop)
  if stop
    metric = stop - start
    report(
      {
        state: latency_state(latency, metric),
        metric: metric,
        description: format('%.3f ms', metric * 1000),
      }.merge(endpoint_report(http, uri, "#{latency} latency")),
    )
  else
    report(
      {
        state: latency_state(latency, nil),
        description: 'timeout',
      }.merge(endpoint_report(http, uri, "#{latency} latency")),
    )
  end
end
report_http_endpoint_response_code(http, uri, response) click to toggle source
# File lib/riemann/tools/http_check.rb, line 182
def report_http_endpoint_response_code(http, uri, response)
  return unless response

  report(
    {
      state: response_code_state(response.code),
      metric: response.code.to_i,
      description: "#{response.code} #{response.message}",
    }.merge(endpoint_report(http, uri, 'response code')),
  )
end
response_code_state(code) click to toggle source
# File lib/riemann/tools/http_check.rb, line 194
def response_code_state(code)
  opts[:response].include?(code) ? 'ok' : 'critical'
end
service(uri, service) click to toggle source
# File lib/riemann/tools/http_check.rb, line 246
def service(uri, service)
  "get #{redact_uri(uri)} #{service}"
end
test_uri_address(uri, address, request) click to toggle source
# File lib/riemann/tools/http_check.rb, line 150
def test_uri_address(uri, address, request)
  response = nil

  start = Time.now
  connected = nil
  done = nil

  http = nil
  begin
    Timeout.timeout(opts[:http_timeout]) do
      http = ::Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https', verify_mode: OpenSSL::SSL::VERIFY_NONE, ipaddr: address)
      connected = Time.now
      response = http.request(request)
    end
  rescue Timeout::Error
    # Ignore
  else
    done = Time.now
  ensure
    http&.finish
  end

  report_http_endpoint_response_code(http, uri, response) if opts[:checks].include?('response-code')
  report_http_endpoint_latency(http, uri, 'connection', start, connected) if opts[:checks].include?('connection-latency')
  report_http_endpoint_latency(http, uri, 'response', start, done) if opts[:checks].include?('response-latency')

  response
rescue StandardError
  # Ignore this address
  nil
end
test_uri_addresses(uri, addresses) click to toggle source
# File lib/riemann/tools/http_check.rb, line 102
def test_uri_addresses(uri, addresses)
  request = ::Net::HTTP::Get.new(uri, { 'user-agent' => opts[:user_agent] })
  request.basic_auth(uri.user, uri.password)

  responses = []

  addresses.each do |address|
    responses << test_uri_address(uri, address.to_s, request)
  end

  responses.compact!

  return unless opts[:checks].include?('consistency')

  raise StandardError, "Could not get any response from #{uri.host}" unless responses.any?

  uniq_code = responses.map(&:code).uniq
  uniq_body = responses.map(&:body).uniq

  issues = []
  issues << "#{uniq_code.count} different response code" unless uniq_code.one?
  issues << "#{uniq_body.count} different response body" unless uniq_body.one?

  if issues.none?
    state = 'ok'
    description = "consistent response on all #{responses.count} endpoints"
  else
    state = 'critical'
    description = "#{issues.join(' and ')} on #{responses.count} endpoints"
  end

  report(
    service: service(uri, 'consistency'),
    state: state,
    description: description,
    hostname: uri.host,
    port: uri.port,
  )
rescue StandardError => e
  report(
    service: service(uri, 'consistency'),
    state: 'critical',
    description: e.message,
    hostname: uri.host,
    port: uri.port,
  )
end
tick() click to toggle source
# File lib/riemann/tools/http_check.rb, line 71
def tick
  report(
    service: 'riemann http-check resolvers utilization',
    metric: (opts[:resolvers].to_f - @resolve_queue.num_waiting) / opts[:resolvers],
    state: @resolve_queue.num_waiting.positive? ? 'ok' : 'critical',
    tags: %w[riemann],
  )
  report(
    service: 'riemann http-check resolvers saturation',
    metric: @resolve_queue.length,
    state: @resolve_queue.empty? ? 'ok' : 'critical',
    tags: %w[riemann],
  )
  report(
    service: 'riemann http-check workers utilization',
    metric: (opts[:workers].to_f - @work_queue.num_waiting) / opts[:workers],
    state: @work_queue.num_waiting.positive? ? 'ok' : 'critical',
    tags: %w[riemann],
  )
  report(
    service: 'riemann http-check workers saturation',
    metric: @work_queue.length,
    state: @work_queue.empty? ? 'ok' : 'critical',
    tags: %w[riemann],
  )

  opts[:uri].each do |uri|
    @resolve_queue.push(URI(uri))
  end
end