Expand HTTP status agent (#1521)

* Add WIP support for HTTP headers

* Actually pass the header to check_this_url

* Fix an unmerged hunk

* Fix some syntax errors

* Fix an outdated variable name

* Comment on which sections do what

* Get rid of (another) unmerged hunk

* Show a form element for the header field

* Fix event emitter conditional

* Adjust tests for header logic

* Test for not returning a header

* Refactor payload generation

* Rename 'header' to 'headers'

* Add multiple header support

* Update HttpStatusAgent docs

* Fix (some) failing tests

* Fix remaining tests

* Add specs for HttpStatusAgent's header code

* Super tiny cleanups
This commit is contained in:
Alex Jordan 2016-06-20 00:37:15 -07:00 committed by Andrew Cantino
parent 371bfa34b7
commit 3c8d6655a3
2 changed files with 82 additions and 18 deletions

View file

@ -12,11 +12,12 @@ module Agents
form_configurable :url
form_configurable :disable_redirect_follow, type: :array, values: ['true', 'false']
form_configurable :headers_to_save
description <<-MD
The HttpStatusAgent will check a url and emit the resulting HTTP status code with the time that it waited for a reply.
The HttpStatusAgent will check a url and emit the resulting HTTP status code with the time that it waited for a reply. Additionally, it will optionally emit the value of one or more specified headers.
Specify a `Url` and the Http Status Agent will produce an event with the http status code.
Specify a `Url` and the Http Status Agent will produce an event with the HTTP status code. If you specify one or more `Headers to save` (comma-delimited) as well, that header or headers' value(s) will be included in the event.
The `disable redirect follow` option causes the Agent to not follow HTTP redirects. For example, setting this to `true` will cause an agent that receives a 301 redirect to `http://yahoo.com` to return a status of 301 instead of following the redirect and returning 200.
MD
@ -26,8 +27,11 @@ module Agents
{
"url": "...",
"status": "..."
"elapsed_time": "..."
"status": "...",
"elapsed_time": "...",
"headers": {
"...": "..."
}
}
MD
@ -46,29 +50,47 @@ module Agents
errors.add(:base, "a url must be specified") unless options['url'].present?
end
def header_array(str)
(str || '').split(',').map(&:strip)
end
def check
check_this_url interpolated[:url]
check_this_url interpolated[:url], header_array(interpolated[:headers_to_save])
end
def receive(incoming_events)
incoming_events.each do |event|
interpolate_with(event) do
check_this_url interpolated[:url]
check_this_url interpolated[:url], header_array(interpolated[:headers_to_save])
end
end
end
private
def check_this_url(url)
def check_this_url(url, local_headers)
# Track time
measured_result = TimeTracker.track { ping(url) }
payload = { 'url' => url, 'response_received' => false, 'elapsed_time' => measured_result.elapsed_time }
# Deal with failures
if measured_result.result
create_event payload: { 'url' => url, 'status' => measured_result.status.to_s, 'response_received' => true, 'elapsed_time' => measured_result.elapsed_time }
payload.merge!({ 'response_received' => true, 'status' => measured_result.status.to_s })
# Deal with headers
if local_headers.present?
header_results = measured_result.result.headers.select {|header, value| local_headers.include?(header)}
# Fill in headers that we wanted, but weren't returned
local_headers.each { |header| header_results[header] = nil unless header_results.has_key?(header) }
payload.merge!({ 'headers' => header_results })
end
create_event payload: payload
memory['last_status'] = measured_result.status.to_s
else
create_event payload: { 'url' => url, 'response_received' => false, 'elapsed_time' => measured_result.elapsed_time }
create_event payload: payload
memory['last_status'] = nil
end
end
def ping(url)

View file

@ -7,6 +7,7 @@ describe 'HttpStatusAgent' do
a.service = services(:generic)
a.user = users(:jane)
a.options['url'] = 'http://google.com'
a.options['headers_to_save'] = 'Server'
a.save!
def a.interpolate_with(e, &block)
@ -76,11 +77,12 @@ describe 'HttpStatusAgent' do
before do
def agent.interpolated
@interpolated ||= { :url => SecureRandom.uuid }
@interpolated ||= { :url => SecureRandom.uuid, :headers_to_save => '' }
end
def agent.check_this_url url
def agent.check_this_url url, local_headers
@url = url
@local_headers = local_headers
end
def agent.checked_url
@ -103,10 +105,12 @@ describe 'HttpStatusAgent' do
let(:successful_url) { SecureRandom.uuid }
let(:status_code) { 200 }
let(:header) { SecureRandom.uuid }
let(:header_value) { SecureRandom.uuid }
let(:event_with_a_successful_ping) do
agent.faraday.set(successful_url, Struct.new(:status).new(status_code))
Event.new.tap { |e| e.payload = { url: successful_url } }
agent.faraday.set(successful_url, Struct.new(:status, :headers).new(status_code, {}))
Event.new.tap { |e| e.payload = { url: successful_url, headers_to_save: "" } }
end
let(:events) do
@ -138,6 +142,11 @@ describe 'HttpStatusAgent' do
expect(agent.the_created_events[0][:payload]['elapsed_time']).not_to be_nil
end
it "should not return a header" do
agent.receive events
expect(agent.the_created_events[0][:payload]['headers']).to be_nil
end
describe "but the status code is not 200" do
let(:status_code) { 500 }
@ -160,8 +169,8 @@ describe 'HttpStatusAgent' do
describe "but the ping returns a status code of 0" do
let(:event_with_a_successful_ping) do
agent.faraday.set(successful_url, Struct.new(:status).new(0))
Event.new.tap { |e| e.payload = { url: successful_url } }
agent.faraday.set(successful_url, Struct.new(:status, :headers).new(0, {}))
Event.new.tap { |e| e.payload = { url: successful_url, headers_to_save: "" } }
end
it "should create one event" do
@ -190,8 +199,8 @@ describe 'HttpStatusAgent' do
describe "but the ping returns a status code of -1" do
let(:event_with_a_successful_ping) do
agent.faraday.set(successful_url, Struct.new(:status).new(-1))
Event.new.tap { |e| e.payload = { url: successful_url } }
agent.faraday.set(successful_url, Struct.new(:status, :headers).new(-1, {}))
Event.new.tap { |e| e.payload = { url: successful_url, headers_to_save: "" } }
end
it "should create one event" do
@ -214,7 +223,7 @@ describe 'HttpStatusAgent' do
describe "and with one event with a failing ping" do
let(:failing_url) { SecureRandom.uuid }
let(:event_with_a_failing_ping) { Event.new.tap { |e| e.payload = { url: failing_url } } }
let(:event_with_a_failing_ping) { Event.new.tap { |e| e.payload = { url: failing_url, headers_to_save: "" } } }
let(:events) do
[event_with_a_successful_ping, event_with_a_failing_ping]
@ -249,6 +258,39 @@ describe 'HttpStatusAgent' do
end
describe "with a header specified" do
let(:event_with_a_successful_ping) do
agent.faraday.set(successful_url, Struct.new(:status, :headers).new(status_code, {header => header_value}))
Event.new.tap { |e| e.payload = { url: successful_url, headers_to_save: header } }
end
it "should return the header value" do
agent.receive events
expect(agent.the_created_events[0][:payload]['headers']).not_to be_nil
expect(agent.the_created_events[0][:payload]['headers'][header]).to eq(header_value)
end
end
describe "with existing and non-existing headers specified" do
let(:nonexistant_header) { SecureRandom.uuid }
let(:event_with_a_successful_ping) do
agent.faraday.set(successful_url, Struct.new(:status, :headers).new(status_code, {header => header_value}))
Event.new.tap { |e| e.payload = { url: successful_url, headers_to_save: header + "," + nonexistant_header } }
end
it "should return the existing header's value" do
agent.receive events
expect(agent.the_created_events[0][:payload]['headers'][header]).to eq(header_value)
end
it "should return nil for the nonexistant header" do
agent.receive events
expect(agent.the_created_events[0][:payload]['headers'][nonexistant_header]).to be_nil
end
end
end
describe "validations" do