Class: Riemann::Tools::HttpCheck
- Inherits:
-
Object
- Object
- Riemann::Tools::HttpCheck
- Includes:
- Riemann::Tools
- Defined in:
- lib/riemann/tools/http_check.rb
Constant Summary collapse
- REQUIRED_RUBY_VERSION =
'2.7.0'
Constants included from Riemann::Tools
Instance Attribute Summary
Attributes included from Riemann::Tools
Instance Method Summary collapse
- #address_belongs_to_ignored_asn?(address) ⇒ Boolean
- #endpoint_report(http, uri, service) ⇒ Object
- #endpoint_service(http, uri, service) ⇒ Object
- #get_request(uri) ⇒ Object
-
#initialize ⇒ HttpCheck
constructor
A new instance of HttpCheck.
- #latency_state(name, latency) ⇒ Object
- #redact_uri(uri) ⇒ Object
- #redirect_uri(uri, location) ⇒ Object
- #report_http_endpoint_latency(http, uri, latency, start, stop) ⇒ Object
- #report_http_endpoint_max_redirects(http, uri) ⇒ Object
- #report_http_endpoint_response_code(http, uri, response) ⇒ Object
- #response_code_state(code) ⇒ Object
- #same_origin?(left, right) ⇒ Boolean
- #service(uri, service) ⇒ Object
-
#shutdown ⇒ Object
Under normal operation, we have a single instance of this class for the lifetime of the process.
- #test_uri_address(uri, address, request, redirect_count: 0) ⇒ Object
- #test_uri_addresses(uri, addresses) ⇒ Object
- #tick ⇒ Object
Methods included from Riemann::Tools
#attributes, #endpoint_name, included, #options, #report, #riemann, #run
Constructor Details
#initialize ⇒ HttpCheck
Returns a new instance of HttpCheck.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/riemann/tools/http_check.rb', line 38 def initialize super @resolve_queue = Queue.new @work_queue = Queue.new @resolvers = [] @workers = [] opts[:resolvers].times do @resolvers << Thread.new do loop do uri = @resolve_queue.pop Thread.exit unless uri 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 if opts[:ignored_asn].any? addresses.reject! do |address| address_belongs_to_ignored_asn?(address) end end next if addresses.empty? @work_queue.push([uri, addresses]) end end end opts[:workers].times do @workers << Thread.new do loop do uri, addresses = @work_queue.pop Thread.exit unless uri test_uri_addresses(uri, addresses) end end end end |
Instance Method Details
#address_belongs_to_ignored_asn?(address) ⇒ Boolean
90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/riemann/tools/http_check.rb', line 90 def address_belongs_to_ignored_asn?(address) begin require 'maxmind/geoip2' rescue LoadError raise StandardError, 'MaxMind::GeoIP2 is not available. Please install the maxmind-geoip2 gem for filtering by ASN.' end @reader ||= MaxMind::GeoIP2::Reader.new(database: opts[:geoip_asn_database]) asn = @reader.asn(address.to_s) opts[:ignored_asn].include?(asn&.autonomous_system_number) rescue MaxMind::GeoIP2::AddressNotFoundError false end |
#endpoint_report(http, uri, service) ⇒ Object
329 330 331 332 333 334 335 336 |
# File 'lib/riemann/tools/http_check.rb', line 329 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) ⇒ Object
338 339 340 |
# File 'lib/riemann/tools/http_check.rb', line 338 def endpoint_service(http, uri, service) "get #{redact_uri(uri)} #{endpoint_name(IPAddr.new(http.ipaddr), http.port)} #{service}" end |
#get_request(uri) ⇒ Object
198 199 200 201 202 203 204 |
# File 'lib/riemann/tools/http_check.rb', line 198 def get_request(uri) request = ::Net::HTTP::Get.new(uri, { 'user-agent' => opts[:user_agent] }) request.basic_auth(uri.user, uri.password) request end |
#latency_state(name, latency) ⇒ Object
314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/riemann/tools/http_check.rb', line 314 def latency_state(name, latency) critical_threshold = opts[:"#{name}_latency_critical"] warning_threshold = opts[:"#{name}_latency_warning"] 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) ⇒ Object
346 347 348 349 350 |
# File 'lib/riemann/tools/http_check.rb', line 346 def redact_uri(uri) reported_uri = uri.dup reported_uri.password = '**redacted**' if reported_uri.password reported_uri end |
#redirect_uri(uri, location) ⇒ Object
251 252 253 254 255 256 257 258 259 260 261 |
# File 'lib/riemann/tools/http_check.rb', line 251 def redirect_uri(uri, location) res = URI.parse(location) res.scheme ||= uri.scheme res.host ||= uri.host res.port ||= uri.port res.user ||= res.user res.password ||= res.password res end |
#report_http_endpoint_latency(http, uri, latency, start, stop) ⇒ Object
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/riemann/tools/http_check.rb', line 285 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_max_redirects(http, uri) ⇒ Object
305 306 307 308 309 310 311 312 |
# File 'lib/riemann/tools/http_check.rb', line 305 def report_http_endpoint_max_redirects(http, uri) report( { state: 'critical', description: "Reached the limit of #{opts[:max_redirects]} redirects", }.merge(endpoint_report(http, uri, 'redirects')), ) end |
#report_http_endpoint_response_code(http, uri, response) ⇒ Object
269 270 271 272 273 274 275 276 277 278 279 |
# File 'lib/riemann/tools/http_check.rb', line 269 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.}", }.merge(endpoint_report(http, uri, 'response code')), ) end |
#response_code_state(code) ⇒ Object
281 282 283 |
# File 'lib/riemann/tools/http_check.rb', line 281 def response_code_state(code) opts[:response].include?(code) ? 'ok' : 'critical' end |
#same_origin?(left, right) ⇒ Boolean
263 264 265 266 267 |
# File 'lib/riemann/tools/http_check.rb', line 263 def same_origin?(left, right) left.scheme == right.scheme && left.host == right.host && left.port == right.port end |
#service(uri, service) ⇒ Object
342 343 344 |
# File 'lib/riemann/tools/http_check.rb', line 342 def service(uri, service) "get #{redact_uri(uri)} #{service}" end |
#shutdown ⇒ Object
Under normal operation, we have a single instance of this class for the lifetime of the process. But when testing, we create a new instance for each test, each with its resolvers and worker threads. The test process may end-up with a lot of running threads, hitting the OS limit of max threads by process and being unable to create more thread:
ThreadError: can’t create Thread: Resource temporarily unavailable
To avoid this situation, we provide this method.
114 115 116 117 118 119 120 |
# File 'lib/riemann/tools/http_check.rb', line 114 def shutdown @resolve_queue.close @resolvers.map(&:join) @work_queue.close @workers.map(&:join) end |
#test_uri_address(uri, address, request, redirect_count: 0) ⇒ Object
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/riemann/tools/http_check.rb', line 206 def test_uri_address(uri, address, request, redirect_count: 0) 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') if opts[:follow_redirects] && %w[301 302].include?(response.code) next_uri = redirect_uri(uri, response['Location']) if same_origin?(uri, next_uri) if redirect_count == opts[:max_redirects] report_http_endpoint_max_redirects(http, uri) return nil else response = test_uri_address(next_uri, address, get_request(next_uri), redirect_count: redirect_count + 1) end end end response rescue StandardError # Ignore this address nil end |
#test_uri_addresses(uri, addresses) ⇒ Object
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/riemann/tools/http_check.rb', line 153 def test_uri_addresses(uri, addresses) request = get_request(uri) responses = addresses.map do |address| 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., hostname: uri.host, port: uri.port, ) end |
#tick ⇒ Object
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/riemann/tools/http_check.rb', line 122 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 |