diff --git a/services/pagerduty_v2.rb b/services/pagerduty_v2.rb new file mode 100644 index 0000000..29d1c11 --- /dev/null +++ b/services/pagerduty_v2.rb @@ -0,0 +1,170 @@ +# encoding: utf-8 +require 'time' + +module Librato::Services + class Service::PagerdutyV2 < Librato::Services::Service + + EVENTS_API_URL = "https://events.pagerduty.com/v2/enqueue" + CONTENT_TYPE = { 'Content-Type': 'application/json' } + + ERROR_MSG = { + routing_key: "The 32 character Integration Key (required).", + description: "A description for this integration (require),", + severity: "Perceived status of event [critical, error, warning or info] (required.)" + } + + def receive_validate(errors) + success = true + [:routing_key, :description, :severity].each do |k| + if settings[k].to_s.empty? + errors[k] = ERROR_MSG[k] + success = false + end + end + success + end + + def receive_alert_clear + receive_alert + end + + def receive_alert + raise_config_error unless receive_validate({}) + + if payload[:alert][:version] == 2 + http_post EVENTS_API_URL, body, CONTENT_TYPE + else + log("Only v2 Alerts are support") + return true + end + end + + def body + payload = { + summary: summary, + source: source, + severity: severity, + timestamp: timestamp, + class: event_class, + custom_details: details, + } + + payload[:group] = group unless group.blank? + + { + routing_key: routing_key, + event_action: event_action, + dedup_key: dedup_key, + payload: payload, + links: links, + } + end + + def routing_key + settings[:routing_key] + end + + def event_action + payload[:clear] ? "resolve" : "trigger" + end + + def dedup_key + keys = [settings[:incident_key], payload[:incident_key]].compact + keys.join("-") + end + + def alert_name + payload[:alert][:name] + end + + def summary + summary = alert_name.blank? ? settings[:description] : alert_name + if payload[:triggered_by_user_test] + description = "[Test] " + description + end + summary + end + + def source + sources = [] + payload[:violations].each do |source, _| + sources << source + end + sources.join(":") + end + + def event_class + classes = [] + payload[:violations].each do |_, measurements| + measurements.each do |m| + classes << m[:metric] + end + end + classes.join(":") + end + + def severity + settings[:severity] + end + + def timestamp + Time.at(payload['trigger_time']).iso8601 + end + + def group + settings[:group] + end + + def details + details = { + } + + [:alert, :conditions, :violations].each do |whitelisted| + details[whitelisted] = payload[whitelisted] + end + + if payload[:triggered_by_user_test] + details[:note] = test_alert_message() + end + + unless payload['alert']['description'].blank? + details[:description] = payload['alert']['description'] + end + + details + end + + def links + links = [ + { + href: alert_link(payload['alert']['id']), + text: "Alert URL", + }, + ] + + unless payload['alert']['runbook_url'].blank? + links << { + href: payload['alert']['runbook_url'], + text: "Runbook URL", + } + end + + if payload[:alert][:version] == 1 + links << { + href: payload_link(payload), + text: "Metric URL", + } + end + + links + end + + def log(msg) + if defined?(Rails) + Rails.logger.info(msg) + else + puts(msg) + end + end + end +end diff --git a/test/pagerduty_v2_test.rb b/test/pagerduty_v2_test.rb new file mode 100644 index 0000000..00075a9 --- /dev/null +++ b/test/pagerduty_v2_test.rb @@ -0,0 +1,126 @@ +require File.expand_path('../helper', __FILE__) + +module Librato::Services + class PagerdutyV2Test < Librato::Services::TestCase + def setup + @stubs = Faraday::Adapter::Test::Stubs.new + end + + def test_validations + params = { + routing_key: 'k', + description: 'd', + severity: 's', + } + + 0.upto(params.keys.length - 1) do |i| + opts = {} + 0.upto(i) do |j| + opts[params.keys[j]] = params[params.keys[j]] + end + svc = service(:alert, opts, alert_payload) + errors = {} + ret = svc.receive_validate(errors) + success = i == params.keys.length - 1 + assert_equal(success, ret, "opts not complete: #{opts}") + assert_equal(0, errors.length) if success + end + end + + def test_new_alerts + svc = service(:alert, { + routing_key: 'k', + description: 'Some alert name', + severity: 'test', + group: 'testing', + incident_key: 'globalkey' + }, new_alert_payload) + + @stubs.post svc.class::EVENTS_API_URL do |env| + body = env[:body] + assert_not_nil body + + assert_equal 'k', body[:routing_key] + assert_equal 'trigger', body[:event_action] + assert_equal 'globalkey-foo', body[:dedup_key] + + payload = body[:payload] + assert_not_nil payload + + assert_equal 'Some alert name', payload[:summary] + assert_equal 'foo.bar', payload[:source] + assert_equal 'test', payload[:severity] + assert_equal 12321123, payload[:timestamp] + assert_equal 'testing', payload[:group] + assert_equal 'metric.name', payload[:class] + + custom_details = payload[:custom_details] + assert_not_nil custom_details + + assert_not_nil custom_details[:alert] + assert_not_nil custom_details[:conditions] + assert_not_nil custom_details[:violations] + + links = body[:links] + assert_not_nil links + assert_equal 'https://metrics.librato.com/alerts/123', links[0][:href] + assert_equal 'Alert URL', links[0][:text] + assert_equal 'http://runbooks.com/howtodoit', links[1][:href] + assert_equal 'Runbook URL', links[1][:text] + + [200, {}, ''] + end + + svc.receive_alert + end + + def test_new_alerts_clearing + payload = new_alert_payload.dup + payload[:clear] = "manual" + svc = service(:alert, { + routing_key: 'k', + description: 'Some alert name', + severity: 'test' + }, payload) + + @stubs.post svc.class::EVENTS_API_URL do |env| + body = env[:body] + assert_not_nil body + + assert_equal 'k', body[:routing_key] + assert_equal 'resolve', body[:event_action] + assert_equal 'foo', body[:dedup_key] + + payload = body[:payload] + assert_not_nil payload + + assert_equal 'Some alert name', payload[:summary] + assert_equal 'foo.bar', payload[:source] + assert_equal 'test', payload[:severity] + assert_equal 12321123, payload[:timestamp] + assert_equal 'metric.name', payload[:class] + + custom_details = payload[:custom_details] + assert_not_nil custom_details + + assert_not_nil custom_details[:alert] + assert_not_nil custom_details[:conditions] + assert_not_nil custom_details[:violations] + + links = body[:links] + assert_not_nil links + assert_equal 'https://metrics.librato.com/alerts/123', links[0][:href] + assert_equal 'Alert URL', links[0][:text] + assert_equal 'http://runbooks.com/howtodoit', links[1][:href] + assert_equal 'Runbook URL', links[1][:text] + [200, {}, ''] + end + + svc.receive_alert + end + + def service(*args) + super Librato::Services::Service::PagerdutyV2, *args + end + end +end