From c5f909cda0782a7485b80244b0302fab3c12cf0a Mon Sep 17 00:00:00 2001 From: TJ Saunders Date: Tue, 20 Feb 2018 23:27:14 -0800 Subject: [PATCH] =?UTF-8?q?Issue=20#34:=20Use=20the=20Sensu=20check=20outp?= =?UTF-8?q?ut=20as=20the=20OpsGenie=20alert=20descripti=E2=80=A6=20(#35)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Issue #34: Use the Sensu check output as the OpsGenie alert description field. * Updating Changelog. * Addressing review comments. * Added requested Changelog entry for added command-line option, too. * Updating tests to match changed OpsGenie API response behavior. Sigh. * Missed correcting/updating one of the suite-specific tests. --- .gitignore | 1 + CHANGELOG.md | 5 ++ README.md | 49 ++++++++++++++++++- bin/handler-opsgenie.rb | 30 ++++++++++-- .../create-alert-with-description.json | 1 + .../serverspec/check-heartbeat-shared_spec.rb | 2 +- .../helpers/serverspec/handler-shared_spec.rb | 17 +++++-- .../serverspec/check_heartbeat_spec.rb | 2 +- 8 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 test/fixtures/create-alert-with-description.json diff --git a/.gitignore b/.gitignore index e4b3a80..5d701e9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ /tmp/ *.bundle *.so +*.sw? *.o *.a mkmf.log diff --git a/CHANGELOG.md b/CHANGELOG.md index ac26d08..44e170d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). This CHANGELOG follows the format listed [here](https://github.com/sensu-plugins/community/blob/master/HOW_WE_CHANGELOG.md) ## [Unreleased] +### Changed +- Providing better alert description field using Sensu check output (@Castaglia) + +### Added +- `handler-opsgenie.rb`: Added flag of `--verbose` to enable verbose/debugging output (@Castaglia) ## [4.1.2] - 2018-02-05 ### Fixed diff --git a/README.md b/README.md index a0e657c..fa8e601 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,54 @@ To get this to work you need to specify a few different things. For a list of fi } ``` -## Notes +## OpsGenie Alerts + +How does the handler map the various Sensu values into the OpsGenie +[alerts and alert fields](https://docs.opsgenie.com/docs/alerts-and-alert-fields) created? + +### Message + +The OpsGenie _message_ alert field is comprised of the Sensu client name, and +the Sensu check name, _e.g._: +``` +web01 : check_mysql_access +``` + +### Teams + +The OpsGenie _team_ alert field uses the values in the Sensu check configuration +if any, otherwise it uses the value from the handler configuration. + +### Recipients + +The OpsGenie _recipients_ alert field uses the values in the Sensu check +configuration if any, otherwise it uses the value from the handler +configuration. + +### Alias + +The OpsGenie _alias_ alert is field is comprised of the Sensu client name, +and the Sensu check name to create a unique key, _e.g._: +``` +web01:check_mysql_access +``` +Note that this can be changed via configuration; see notes below. + +### Entity + +The OpsGenie _entity_ alert field uses the Sensu client name. + +### Description + +The OpsGenie _description_ alert field is populated with the Sensu check output. + +### Priority + +The OpsGenie _priority_ alert field is not explicitly set; OpsGenie will thus +assign the default priority of "P3" to the alert. + + +## Configuration Notes If the check definition uses the custom `alias` attribute, _e.g._: ``` diff --git a/bin/handler-opsgenie.rb b/bin/handler-opsgenie.rb index ed84855..9927426 100755 --- a/bin/handler-opsgenie.rb +++ b/bin/handler-opsgenie.rb @@ -11,7 +11,7 @@ require 'erb' class Opsgenie < Sensu::Handler - attr_reader :json_config, :message_template + attr_reader :json_config, :message_template, :verbose OPSGENIE_URL = 'https://api.opsgenie.com/v2/alerts'.freeze @@ -27,6 +27,12 @@ class Opsgenie < Sensu::Handler long: '--template ', default: nil + option :verbose, + description: 'Enable verbose/debugging output', + short: '-v', + long: '--verbose', + default: false + def handle init process @@ -66,6 +72,11 @@ def process puts "opsgenie -- timed out while attempting to #{@event['action']} a incident -- #{event_id}" end + def description + return json_config['description'] unless json_config['description'].nil? + @event['check']['output'].chomp + end + def message return @event['notification'] unless @event['notification'].nil? return default_message if message_template.nil? || !File.exist?(message_template) @@ -79,7 +90,7 @@ def custom_message end def default_message - [@event['client']['name'], @event['check']['name'], @event['check']['output'].chomp].join(' : ') + [@event['client']['name'], @event['check']['name']].join(' : ') end def event_id @@ -111,7 +122,7 @@ def create_alert post_to_opsgenie(:create, alias: event_id, message: message, - description: json_config['description'], + description: description, entity: client_name, tags: tags, recipients: json_config['recipients'], @@ -145,6 +156,19 @@ def post_to_opsgenie(action = :create, params = {}) "#{encoded_alias}/close?identifierType=alias" end uri = URI.parse("#{OPSGENIE_URL}/#{uripath}") + + if config[:verbose] + # Note that the ordering of these lines roughly follows the order + # alert fields are displayed in the OpsGenie web UI. + puts "URL: #{uri}" + puts "Message: #{params[:message]}" + puts "Tags: #{params[:tags]}" + puts "Entity: #{params[:entity]}" + puts "Teams: #{params[:teams]}" + puts "Alias: #{params[:alias]}" + puts "Description: #{params[:description]}" + end + http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE diff --git a/test/fixtures/create-alert-with-description.json b/test/fixtures/create-alert-with-description.json new file mode 100644 index 0000000..9486ad1 --- /dev/null +++ b/test/fixtures/create-alert-with-description.json @@ -0,0 +1 @@ +{"client":{"name":"test01","address":"127.0.0.1","subscriptions":["all"],"timestamp":1326390159},"check":{"name":"some.fake.check.name","issued":1326390169,"output":"CRITICAL: text\n","status":2,"notification":"check failed","command":"/path/to/some/stuff/here -A do_smoke","subscribers":["all"],"interval":60,"handlers":["default","opsgenie"],"history":["0","0","2"],"flapping":false,"opsgenie":{"description":"MY_CUSTOM_DESCRIPTION"}},"occurrences":1,"action":"create"} diff --git a/test/integration/helpers/serverspec/check-heartbeat-shared_spec.rb b/test/integration/helpers/serverspec/check-heartbeat-shared_spec.rb index c782240..0aac876 100644 --- a/test/integration/helpers/serverspec/check-heartbeat-shared_spec.rb +++ b/test/integration/helpers/serverspec/check-heartbeat-shared_spec.rb @@ -15,6 +15,6 @@ describe 'check-opsgenie-heartbeat' do describe command("#{check} --key #{key} --name #{name}") do its(:exit_status) { should eq 3 } - its(:stdout) { should match(/UNKNOWN: unexpected response code 401/) } + its(:stdout) { should match(/UNKNOWN: unexpected response code (401|422)/) } end end diff --git a/test/integration/helpers/serverspec/handler-shared_spec.rb b/test/integration/helpers/serverspec/handler-shared_spec.rb index f11d5db..51fcd2f 100644 --- a/test/integration/helpers/serverspec/handler-shared_spec.rb +++ b/test/integration/helpers/serverspec/handler-shared_spec.rb @@ -12,26 +12,35 @@ create_alert_with_alias_file = '/tmp/kitchen/data/test/fixtures/create-alert-with-alias.json' resolve_alert_with_alias_file = '/tmp/kitchen/data/test/fixtures/resolve-alert-with-alias.json' +create_alert_with_description_file = '/tmp/kitchen/data/test/fixtures/create-alert-with-description.json' + # These tests would require a valid OpsGenie API key and heartbeat name # configured in order to succeed. Thus for now, we limit ourselves to the # expected failure cases. describe 'handler-opsgenie' do default_event_id_pattern = 'test01:some\.fake\.check\.name' - describe command("#{handler} < #{create_alert_file}") do + describe command("#{handler} -v < #{create_alert_file}") do + its(:stdout) { should match(/Description:.*CRITICAL.*text/) } its(:stdout) { should match(/failed to create incident.*#{default_event_id_pattern}.*not authorized/) } end - describe command("#{handler} < #{resolve_alert_file}") do + describe command("#{handler} -v < #{resolve_alert_file}") do its(:stdout) { should match(/failed to resolve incident.*#{default_event_id_pattern}.*not authorized/) } end custom_event_id_pattern = 'MY_CUSTOM_ALIAS' - describe command("#{handler} < #{create_alert_with_alias_file}") do + describe command("#{handler} -v < #{create_alert_with_alias_file}") do its(:stdout) { should match(/failed to create incident.*#{custom_event_id_pattern}.*not authorized/) } end - describe command("#{handler} < #{resolve_alert_with_alias_file}") do + describe command("#{handler} -v < #{resolve_alert_with_alias_file}") do its(:stdout) { should match(/failed to resolve incident.*#{custom_event_id_pattern}.*not authorized/) } end + + custom_description_pattern = 'MY_CUSTOM_DESCRIPTION' + describe command("#{handler} -v < #{create_alert_with_description_file}") do + its(:stdout) { should_not match(/Description:.*CRITICAL.*text/) } + its(:stdout) { should match(/Description:.*#{custom_description_pattern}/) } + end end diff --git a/test/integration/ruby-21/serverspec/check_heartbeat_spec.rb b/test/integration/ruby-21/serverspec/check_heartbeat_spec.rb index c782240..0aac876 100644 --- a/test/integration/ruby-21/serverspec/check_heartbeat_spec.rb +++ b/test/integration/ruby-21/serverspec/check_heartbeat_spec.rb @@ -15,6 +15,6 @@ describe 'check-opsgenie-heartbeat' do describe command("#{check} --key #{key} --name #{name}") do its(:exit_status) { should eq 3 } - its(:stdout) { should match(/UNKNOWN: unexpected response code 401/) } + its(:stdout) { should match(/UNKNOWN: unexpected response code (401|422)/) } end end