Skip to content

Commit

Permalink
Sensu Handler shim for Sensu 2.0 event data. (#190)
Browse files Browse the repository at this point in the history
* Added event_2to1 method to Utils and --enable-2.0-event option to base Handler class. New option makes it possible to use sensu-plugin based handlers with Sensu 2.0 events until handlers provide native 2.0 event support.

* handle case when event['client'] is nil

* Refactor event_2to1 utility function to take optional event argument

* fix bad conditional for expected integer attribute

* add test for --enable-2to1-mapping handler argument

* fix unreleased changelog entry format

* remove unneeded cornercase mapping due to now resolved attribute naming issue

* fix for handle 2to1 test

* Refactor function name from awkward 2to1 as per pr review

* return orig_event if already mapped

* fix argument to match refactor

* make it possible to set env variable to attempt v2 into v1 mapping automatically

* update changelog with more detail on added v2 -> v1 mapping support

* add support for v2 -> v1 event mapping to mutator class, and update changelog

* remove unneeded confusing unless conditional

* fix for stray comma

* refactor state to action mapping to a use a 1:1 hash lookup with a fallback if state is not in understood mapping

* refactor history handling, save original history as history_v2.

* extend 2to1 test coverage to include history
  • Loading branch information
jspaleta authored and majormoses committed Sep 13, 2018
1 parent c3a2508 commit 552f5fa
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 0 deletions.
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.
- 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)
new_event = mutator.map_v2_event_into_v1
mutator.event = new_event
end

mutator.mutate
mutator.dump
end
Expand Down
94 changes: 94 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,92 @@ 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 if enity exists and client does not
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
##
action_state_mapping = {
'flapping' => 'flapping',
'passing' => 'resolve',
'failing' => 'create'
}

state = event['check']['state'] || 'unknown::2.0_event'

# Attempt to map 2.0 event state to 1.4 event action
event['action'] ||= action_state_mapping[state.downcase]
# Fallback action is 2.0 event state
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']
# Let's save the original history
history_v2 = Marshal.load(Marshal.dump(event['check']['history']))
event['check']['history_v2'] = history_v2
legacy_history = []
event['check']['history'].each do |h|
legacy_history << h['status'].to_i.to_s || '3'
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
33 changes: 33 additions & 0 deletions test/external/handle-2to1
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/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('^')
history = @event['check']['history'].join('')
[client_name, check_name, source, output, total_state_change, action, client_subscribers, check_subscribers, history].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":4,"state":"failing","history":[{"status":0,"executed":0},{"status":1,"executed":1},{"status":2,"executed":2},{"status":3,"executed":3},{"status":0,"executed":4}],"status":0}}
18 changes: 18 additions & 0 deletions test/handle_2to1_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
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 = JSON.parse(fixture('basic_v2_event.json').read)
expected = "test_entity : test_check : test_proxy : test_output : 4 : create : sub1|sub2|sub3 : sub1^sub2^sub3 : 01230\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

0 comments on commit 552f5fa

Please sign in to comment.