Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
require "rest-client" | ||
require "json" | ||
|
||
module Agents | ||
class PingdomAgent < Agent | ||
cannot_receive_events! | ||
|
||
description <<-MD | ||
The Pingdom Agent creates an event for grabbing Pingdom alerts from an account specified by the given `pingdom_credref` field. This field is a key for the Pingdom password stored under [credentials](/user_credentials). | ||
An API key set in `pingdom_apikey` is also necessary. You can generate a key under your Pingdom account by [following instructions found here](https://my.pingdom.com/account/appkeys). | ||
An event will be created for each check that has changed state since the previous check. `memory['last_run']` is utilized to keep track of the last Pingdom check. | ||
MD | ||
|
||
event_description <<-MD | ||
Events look like this: | ||
{ | ||
"name": "Build.com", | ||
"state": "up", | ||
"lastresponsetime": "796ms" | ||
} | ||
MD | ||
|
||
default_schedule "every_1m" | ||
|
||
def working? | ||
event_created_within?(options['expected_update_period_in_days']) && !recent_error_logs? | ||
end | ||
|
||
def pingdom_setup? | ||
options['pingdom_url'].present? && pingdom_credref.present? && options['pingdom_apikey'].present? | ||
end | ||
|
||
def default_options | ||
{ | ||
'pingdom_url' => 'https://api.pingdom.com/api/2.0', | ||
'pingdom_credref' => '-email-', | ||
'pingdom_apikey' => '', | ||
'expected_update_period_in_days' => '14', | ||
} | ||
end | ||
|
||
def pingdom_credref | ||
(options['pingdom_credref'].presence && (options['pingdom_credref'] != '-email-')) ? options['pingdom_credref'] : nil | ||
end | ||
|
||
def pingdom_credentials | ||
if pingdom_credref.present? | ||
{ | ||
'pingdom_user' => options['pingdom_credref'], | ||
'pingdom_pass' => credential(options['pingdom_credref']) | ||
} | ||
end | ||
end | ||
|
||
def pingdom_url(path) | ||
"#{options["pingdom_url"]}/#{path}" | ||
end | ||
|
||
def validate_options | ||
errors.add(:base, "Pingdom URL and credential reference are required") unless pingdom_setup? | ||
errors.add(:base, "You need to specify the expected update period") unless options['expected_update_period_in_days'].present? | ||
end | ||
|
||
def get(url, options) | ||
response = RestClient::Request.new( | ||
:method => :get, | ||
:url => url, | ||
:user => pingdom_credentials['pingdom_user'], | ||
:password => pingdom_credentials['pingdom_pass'], | ||
:headers => options).execute | ||
|
||
if response.code == 400 | ||
raise RuntimeError.new("Pingdom error: #{response['errorMessages']}") | ||
elsif response.code == 403 | ||
raise RuntimeError.new("Authentication failed: Forbidden (403)") | ||
elsif response.code != 200 | ||
raise RuntimeError.new("Request failed: #{response}") | ||
end | ||
|
||
response.body | ||
end | ||
|
||
def get_checks() | ||
checks = {} | ||
if pingdom_setup? | ||
response = JSON.parse( | ||
get(pingdom_url('checks'), {"App-Key" => options['pingdom_apikey'], :content_type => :json}), | ||
:symbolize_names => true) | ||
if response[:checks] | ||
response[:checks].zip(response[:checks]) { |a,b| | ||
checks[a[:name].to_sym] = { | ||
:state => (b[:status] == 'up') ? 'up' : 'down', | ||
:lastresponsetime => (b[:status] == 'up') ? b[:lastresponsetime] : "DOWN" | ||
} | ||
} | ||
else | ||
checks["pingdom"] = {:state => "down", :lastresponsetime => "-"} | ||
end | ||
end | ||
return checks | ||
end | ||
|
||
def check | ||
if pingdom_setup? | ||
if memory.has_key?("last_run") | ||
last_run = JSON.parse(memory["last_run"]) | ||
else | ||
last_run = {} | ||
end | ||
checks = get_checks() | ||
# Only create event if the state has changed. | ||
checks.each do |check, chkinfo| | ||
checkkey = check.to_s | ||
if last_run.has_key?(checkkey) | ||
if last_run[checkkey]["state"] != chkinfo[:state] | ||
log "#{check} transitioning from #{last_run[checkkey]["state"]} to #{chkinfo[:state]}" | ||
create_event :payload => chkinfo.merge(:name => check) | ||
end | ||
else | ||
log "#{check} initializing to #{chkinfo[:state]}" | ||
create_event :payload => chkinfo.merge(:name => check) | ||
end | ||
end | ||
memory["last_run"] = JSON.generate(checks) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"checks":[{"id":94638,"created":1245088418,"name":"FaucetDirect.com","hostname":"www.faucetdirect.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1401698723,"lasttesttime":1401771767,"lastresponsetime":794,"status":"up","tags":[]},{"id":114126,"created":1252561766,"name":"FaucetDirect Search","hostname":"www.faucetdirect.com","use_legacy_notifications":true,"resolution":5,"type":"http","lasterrortime":1401257527,"lasttesttime":1401771722,"lastresponsetime":713,"status":"up","tags":[]},{"id":94639,"created":1245088532,"name":"LightingDirect.com","hostname":"www.lightingdirect.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1401257745,"lasttesttime":1401771765,"lastresponsetime":287,"status":"up","tags":[]},{"id":241921,"created":1289945581,"name":"LightingDirect Search","hostname":"www.lightingdirect.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1401755432,"lasttesttime":1401771752,"lastresponsetime":534,"status":"up","tags":[]},{"id":94642,"created":1245092107,"name":"OMC","hostname":"www.omconsole.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1400401419,"lasttesttime":1401771760,"lastresponsetime":560,"status":"up","tags":[]},{"id":94674,"created":1245102658,"name":"VentingDirect.com","hostname":"www.VentingDirect.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1401257593,"lasttesttime":1401771733,"lastresponsetime":285,"status":"up","tags":[]},{"id":94678,"created":1245103071,"name":"FTP Server","hostname":"ftp.improvementdirect.com","use_legacy_notifications":true,"resolution":5,"type":"tcp","lasterrortime":1396901076,"lasttesttime":1401771576,"lastresponsetime":112,"status":"up","tags":[]},{"id":190374,"created":1276623657,"name":"Faucet.com","hostname":"www.faucet.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1400400467,"lasttesttime":1401771767,"lastresponsetime":256,"status":"up","tags":[]},{"id":155191,"created":1266302703,"name":"PullsDirect Search","hostname":"www.pullsdirect.com","use_legacy_notifications":true,"resolution":5,"type":"http","lasterrortime":1401257523,"lasttesttime":1401771715,"lastresponsetime":517,"status":"up","tags":[]},{"id":620780,"created":1344272572,"name":"PullsDirect.com","hostname":"www.pullsdirect.com","use_legacy_notifications":false,"resolution":1,"type":"http","lasterrortime":1401257947,"lasttesttime":1401771727,"lastresponsetime":725,"status":"up","tags":[]},{"id":198729,"created":1279216694,"name":"Build.com","hostname":"www.build.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1401257688,"lasttesttime":1401771768,"lastresponsetime":336,"status":"up","tags":[]},{"id":1182720,"created":1397071551,"name":"Test Check","hostname":"www.build.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1401257798,"lasttesttime":1401771758,"lastresponsetime":229,"status":"up","tags":[]},{"id":200917,"created":1279817292,"name":"Build Corp","hostname":"corp.build.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1401257588,"lasttesttime":1401771728,"lastresponsetime":474,"status":"up","tags":[]},{"id":387083,"created":1312176274,"name":"LightingShowplace.com","hostname":"www.lightingshowplace.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1400400446,"lasttesttime":1401771746,"lastresponsetime":582,"status":"up","tags":[]},{"id":553866,"created":1335906463,"name":"Floormall.com","hostname":"www.floormall.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1401257914,"lasttesttime":1401771770,"lastresponsetime":425,"status":"up","tags":[]},{"id":554890,"created":1335999944,"name":"Image Farm 1","hostname":"s1.img-b.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasttesttime":1401771734,"lastresponsetime":164,"status":"up","tags":[]},{"id":554891,"created":1336000033,"name":"Image Farm 2","hostname":"s2.img-b.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasttesttime":1401771755,"lastresponsetime":32,"status":"up","tags":[]},{"id":554895,"created":1336000625,"name":"Build Mobile","hostname":"m.build.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1400402093,"lasttesttime":1401771732,"lastresponsetime":118,"status":"up","tags":[]},{"id":770979,"created":1361765567,"name":"Chico Office - Otterson","hostname":"50.203.70.193","use_legacy_notifications":true,"resolution":1,"type":"ping","lasterrortime":1397736227,"lasttesttime":1401771751,"lastresponsetime":160,"status":"up","tags":[]},{"id":1015443,"created":1383536804,"name":"build.ca","hostname":"www.build.ca","use_legacy_notifications":true,"resolution":5,"type":"http","lasterrortime":1397062022,"lasttesttime":1401771562,"lastresponsetime":622,"status":"up","tags":[]},{"id":94672,"created":1245102550,"name":"Handlesets.com","hostname":"www.handlesets.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1401257647,"lasttesttime":1401771727,"lastresponsetime":354,"status":"up","tags":[]},{"id":94676,"created":1245102840,"name":"VentingPipe.com","hostname":"www.ventingpipe.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1401257786,"lasttesttime":1401771746,"lastresponsetime":356,"status":"up","tags":[]},{"id":126426,"created":1257461901,"name":"Handlesets Search","hostname":"www.handlesets.com","use_legacy_notifications":true,"resolution":1,"type":"http","lasterrortime":1401257786,"lasttesttime":1401771746,"lastresponsetime":486,"status":"up","tags":[]}]} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
require 'spec_helper' | ||
|
||
describe Agents::PingdomAgent do | ||
before(:each) do | ||
stub_request(:get, /api.pingdom.com/).to_return(:body => File.read(Rails.root.join("spec/data_fixtures/pingdom_checks.json")), :status => 200, :headers => {"Content-Type" => "text/json"}) | ||
|
||
@valid_params = { | ||
:pingdom_url => 'https://api.pingdom.com/api/2.0', | ||
:pingdom_credref => 'user@somewhere.com', | ||
:pingdom_apikey => 'key4you...', | ||
:expected_update_period_in_days => '14', | ||
} | ||
|
||
@checker = Agents::PingdomAgent.new(:name => "pingdom-agent", :options => @valid_params) | ||
@checker.user = users(:jane) | ||
@checker.save! | ||
end | ||
|
||
describe "validating" do | ||
before do | ||
@checker.should be_valid | ||
end | ||
|
||
it "should require the pingdom_credref" do | ||
@checker.options['pingdom_credref'] = nil | ||
@checker.should_not be_valid | ||
end | ||
|
||
it "should require the pingdom url" do | ||
@checker.options['pingdom_url'] = nil | ||
@checker.should_not be_valid | ||
end | ||
|
||
it "should require the pingdom apikey" do | ||
@checker.options['pingdom_apikey'] = nil | ||
@checker.should_not be_valid | ||
end | ||
|
||
it "should require the expected_update_period_in_days" do | ||
@checker.options['expected_update_period_in_days'] = nil | ||
@checker.should_not be_valid | ||
end | ||
end | ||
|
||
describe "helpers" do | ||
it "should generate a valid credential reference" do | ||
@checker.send(:pingdom_credref).should == "user@somewhere.com" | ||
end | ||
|
||
it "should generate a correct request url" do | ||
@checker.send(:pingdom_url, 'checks').should == "https://api.pingdom.com/api/2.0/checks" | ||
end | ||
end | ||
|
||
describe "#check" do | ||
it "should be able to retrieve issues" do | ||
reply = File.read(Rails.root.join("spec/data_fixtures/pingdom_checks.json")) | ||
mock(@checker).get("https://api.pingdom.com/api/2.0/checks", {"App-Key"=>"key4you...", :content_type => :json}).returns(reply) | ||
|
||
expect { @checker.check }.to change { Event.count }.by(23) | ||
end | ||
end | ||
|
||
describe '#working?' do | ||
it 'checks if events have been received within the expected receive period' do | ||
@checker.should_not be_working # No events received | ||
@checker.check | ||
@checker.reload.should be_working # Just received events | ||
fourteen_days_from_now = 14.days.from_now | ||
stub(Time).now { fourteen_days_from_now } | ||
@checker.reload.should_not be_working # More time has passed than the expected receive period without any new events | ||
end | ||
end | ||
end |