Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sensu Handler shim for Sensu 2.0 event data. #190

Merged
merged 28 commits into from
Sep 13, 2018
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
feb1b95
Added event_2to1 method to Utils and --enable-2.0-event option to bas…
Jul 24, 2018
d0d531e
cleanup for rubocop
Jul 24, 2018
339ead8
cleanup for rubocop
Jul 24, 2018
06dcf07
handle case when event['client'] is nil
Jul 25, 2018
01fb9e1
Refactor event_2to1 utility function to take optional event argument
Sep 4, 2018
c165f1e
Merge pull request #1 from sensu-plugins/master
jspaleta Sep 4, 2018
bfa7433
Merge branch 'master' into handler_shim_for_2.0_events
Sep 4, 2018
009fb83
fix bad conditional for expected integer attribute
Sep 5, 2018
65cc0d7
add test for --enable-2to1-mapping handler argument
Sep 5, 2018
92cb685
clean up for rubocop
Sep 5, 2018
ac18b2e
fix unreleased changelog entry format
Sep 5, 2018
c0f891b
remove unneeded cornercase mapping due to now resolved attribute nami…
Sep 5, 2018
40ba5b8
fix for handle 2to1 test
Sep 5, 2018
953b1d2
Refactor function name from awkward 2to1 as per pr review
Sep 5, 2018
68152d2
return orig_event if already mapped
Sep 5, 2018
5be70d9
fix argument to match refactor
Sep 5, 2018
a160133
make it possible to set env variable to attempt v2 into v1 mapping au…
Sep 5, 2018
fd160df
rubocop fix ups
Sep 5, 2018
2a39832
update changelog with more detail on added v2 -> v1 mapping support
Sep 5, 2018
6203e84
add support for v2 -> v1 event mapping to mutator class, and update c…
Sep 5, 2018
f4a7d68
small fix for rubocop
Sep 5, 2018
a6d8b07
remove unneeded confusing unless conditional
Sep 6, 2018
b951c11
fix for rubocop
Sep 6, 2018
acb27c3
fix for stray comma
Sep 9, 2018
0af1f41
refactor state to action mapping to a use a 1:1 hash lookup with a fa…
Sep 9, 2018
7ef0723
refactor history handling, save original history as history_v2.
Sep 9, 2018
38d5a97
fix for rubocop
Sep 9, 2018
21fca96
extend 2to1 test coverage to include history
Sep 9, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Added
- Added map_v2_event_into_v1 method to Utils for all plugin classes to use.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add attribution for you post acceptance or you can do it prior, either way works.

- Added --map-v2-event-into-v1 runtime commandline option to base Handler and Mutator classes.
- Alternatively set envvar SENSU_MAP_V2_EVENT_INTO_V1=1 and handlers/mutators will automatically attempt to map 2.x event data.
- New cli option/envvar makes it possible to use sensu-plugin based handlers/mutators
with Sensu 2.0 events until they provide native 2.0 event support internally.
- Mapping function sets and checks for boolean event attribute 'v2_event_mapped_into_v1',
to prevent mapping from running multiple times in same pipeline.

## [2.6.0] - 2018-08-28
### Fixed
Expand Down
12 changes: 12 additions & 0 deletions lib/sensu-handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ module Sensu
class Handler
include Sensu::Plugin::Utils
include Mixlib::CLI
option :map_v2_event_into_v1,
description: 'Enable 2.x to 1.4 event mapping. Alternatively set envvar SENSU_MAP_V2_EVENT_INTO_V1=1.',
boolean: true,
long: '--map-v2-event-into-v1'

attr_accessor :argv

Expand Down Expand Up @@ -73,6 +77,14 @@ def self.disable_autorun
if @@autorun
handler = @@autorun.new
handler.read_event(STDIN)

TRUTHY_VALUES = %w[1 t true yes y].freeze
automap = ENV['SENSU_MAP_V2_EVENT_INTO_V1'].to_s.downcase

if handler.config[:map_v2_event_into_v1,] || TRUTHY_VALUES.include?(automap)
new_event = handler.map_v2_event_into_v1
handler.event = new_event
end
handler.filter
handler.handle
end
Expand Down
13 changes: 13 additions & 0 deletions lib/sensu-mutator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ module Sensu
class Mutator
include Sensu::Plugin::Utils
include Mixlib::CLI
option :map_v2_event_into_v1,
description: 'Enable 2.x to 1.4 event mapping. Alternatively set envvar SENSU_MAP_V2_EVENT_INTO_V1=1.',
boolean: true,
long: '--map-v2-event-into-v1'

attr_accessor :argv

Expand Down Expand Up @@ -67,6 +71,15 @@ def self.disable_autorun
return unless @@autorun
mutator = @@autorun.new
mutator.read_event(STDIN)

TRUTHY_VALUES = %w[1 t true yes y].freeze
automap = ENV['SENSU_MAP_V2_EVENT_INTO_V1'].to_s.downcase

if mutator.config[:map_v2_event_into_v1,] || TRUTHY_VALUES.include?(automap)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

, looks like a mistake

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting that this is allowed syntax by ruby. Fixed in newer commits.

new_event = mutator.map_v2_event_into_v1
mutator.event = new_event
end

mutator.mutate
mutator.dump
end
Expand Down
84 changes: 84 additions & 0 deletions lib/sensu-plugin/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ def settings
@settings ||= config_files.map { |f| load_config(f) }.reduce { |a, b| deep_merge(a, b) }
end

def event
@event
end

def event=(value)
@event = value
end

def read_event(file)
@event = ::JSON.parse(file.read)
@event['occurrences'] ||= 1
Expand All @@ -33,6 +41,82 @@ def read_event(file)
exit 0
end

##
# Helper method to convert Sensu 2.0 event into Sensu 1.4 event
# This is here to help keep Sensu Plugin community handlers working
# until they natively support 2.0
# Takes 2.0 event json object as argument
# Returns event with 1.4 mapping included
#
# Note:
# The 1.4 mapping overwrites some attributes so the resulting event cannot
# be used in a 2.0 workflow. The top level boolean attribute "v2_event_mapped_into_v1"
# will be set to true as a hint to indicate this is a mapped event object.
#
##
def map_v2_event_into_v1(orig_event = nil)
orig_event ||= @event

# return orig_event if already mapped
return orig_event if orig_event['v2_event_mapped_into_v1']

# Deep copy of orig_event
event = Marshal.load(Marshal.dump(orig_event))

# Trigger mapping code iff enity exists and client does not
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like an extra f

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

client_missing = event['client'].nil? || event['client'].empty?
if event.key?('entity') && client_missing
##
# create the client hash from the entity hash
##
event['client'] = event['entity']

##
# Fill in missing client attributes
##
event['client']['name'] ||= event['entity']['id']
event['client']['subscribers'] ||= event['entity']['subscriptions']

##
# Fill in renamed check attributes expected in 1.4 event
# subscribers, source
##
event['check']['subscribers'] ||= event['check']['subscriptions']
event['check']['source'] ||= event['check']['proxy_entity_id']

##
# Mimic 1.4 event action based on 2.0 event state
# action used in logs and fluentd plugins handlers
##
state = event['check']['state'] || 'unknown::2.0_event'
event['action'] ||= 'flapping' if state.casecmp('flapping').zero?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this could be much simpler and less error prone with a case statement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactored using a hash mapping, since the states to action are 1 to 1.

event['action'] ||= 'resolve' if state.casecmp('passing').zero?
event['action'] ||= 'create' if state.casecmp('failing').zero?
event['action'] ||= state

##
# Mimic 1.4 event history based on 2.0 event history
# Note: This overwrites the same history attribute
# 2.x history is an array of hashes, each hash includes status
# 1.x history is an array of statuses
##
if event['check']['history']
legacy_history = []
event['check']['history'].each do |h|
legacy_history << h['status'].to_s || '3'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm if we are not sure what the history is I feel like we should use nil (though that might cause issues?) as not knowing how to translate is not quite the same as the check itself returning an unknown state.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also is history really stored as the string when really they are integers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the 1.4 events I have access to, the are stored as strings.
there is at least one mutator in the sensu-plugins collection that makes use of the history, and in fact makes a comment that the history is saved as an array strings.

sensu-plugins-pagerduty/bin/mutator-pagerduty-priority-override.rb

Returning nil instead of '3' may cause weird issues nil.to_i gets translated to 0 which is the 'ok' status. I think if we don't know how to translate the status in the history its better to use the 3 aka 'unknown' to make sure we flag it as a problem that needs to be investigated.

Or it may be better to throw an error here instead of providing a fallback if the status is unmappable to a string.

end
event['check']['history'] = legacy_history
end

##
# Setting flag indicating this function has already been called
##
event['v2_event_mapped_into_v1'] = true
end
# return the updated event
event
end

def net_http_req_class(method)
case method.to_s.upcase
when 'GET'
Expand Down
32 changes: 32 additions & 0 deletions test/external/handle-2to1
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env ruby
require 'sensu-handler'

class Helpers < Sensu::Handler
def handle
puts event_summary
end

def api_request(*_args)
nil
end

def stash_exists?(*_args)
nil
end

def event_exists?(*_args)
true
end

def event_summary
client_name = @event['client']['name']
check_name = @event['check']['name']
source = @event['check']['source']
output = @event['check']['output']
total_state_change = @event['check']['total_state_change']
action = @event['action']
client_subscribers = @event['client']['subscribers'].join('|')
check_subscribers = @event['client']['subscribers'].join('^')
[client_name, check_name, source, output, total_state_change, action, client_subscribers, check_subscribers].join(' : ')
end
end
1 change: 1 addition & 0 deletions test/fixtures/basic_v2_event.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"entity":{"id":"test_entity","subscriptions":["sub1","sub2","sub3"]},"check":{"name":"test_check","output":"test_output","subscriptions":["sub1","sub2","sub3"],"proxy_entity_id":"test_proxy","total_state_change":1,"state":"failing","status":0}}
34 changes: 34 additions & 0 deletions test/handle_2to1_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require 'test_helper'
require 'English'

class TestHandle2to1 < MiniTest::Test
include SensuPluginTestHelper

def setup
set_script 'external/handle-2to1 --map-v2-event-into-v1'
end

def test_2to1_enabled
event = {
'entity' => {
'id' => 'test_entity',
'subscriptions' => ['sub1', 'sub2', 'sub3']
},
'check' => {
'name' => 'test_check',
'output' => 'test_output',
'subscriptions' => ['sub1', 'sub2', 'sub3'],
'proxy_entity_id' => 'test_proxy',
'total_state_change' => 1,
'state' => 'failing',
'status' => 0
},
'occurrences' => 1,
'action' => 'create'
}
expected = "test_entity : test_check : test_proxy : test_output : 1 : create : sub1|sub2|sub3 : sub1^sub2^sub3\n"
output = run_script_with_input(JSON.generate(event))
assert_equal(0, $CHILD_STATUS.exitstatus)
assert_match(expected, output)
end
end
24 changes: 24 additions & 0 deletions test/mutator_2to1_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env ruby
require 'English'

require 'test_helper'

# Simple Heper to test mutator
class TestMutatorHelpers < MiniTest::Test
include SensuPluginTestHelper
def test_base_2to1_mutator
set_script 'external/mutator-trivial --map-v2-event-into-v1'
event = JSON.parse(fixture('basic_v2_event.json').read)
output = run_script_with_input(JSON.generate(event))
assert_equal(0, $CHILD_STATUS.exitstatus)
assert_equal(event['entity']['id'], JSON.parse(output)['client']['name'])
end

def test_external_2to1_mutator
set_script 'external/mutator-helpers --map-v2-event-into-v1'
event = JSON.parse(fixture('basic_v2_event.json').read)
output = run_script_with_input(JSON.generate(event))
assert_equal(0, $CHILD_STATUS.exitstatus)
assert_equal(true, JSON.parse(output)['mutated'])
end
end