Skip to content

Commit 552f5fa

Browse files
jspaletamajormoses
authored andcommitted
Sensu Handler shim for Sensu 2.0 event data. (#190)
* 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
1 parent c3a2508 commit 552f5fa

File tree

8 files changed

+203
-0
lines changed

8 files changed

+203
-0
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file.
33
This project adheres to [Semantic Versioning](http://semver.org/).
44

55
## [Unreleased]
6+
### Added
7+
- Added map_v2_event_into_v1 method to Utils for all plugin classes to use.
8+
- Added --map-v2-event-into-v1 runtime commandline option to base Handler and Mutator classes.
9+
- Alternatively set envvar SENSU_MAP_V2_EVENT_INTO_V1=1 and handlers/mutators will automatically attempt to map 2.x event data.
10+
- New cli option/envvar makes it possible to use sensu-plugin based handlers/mutators
11+
with Sensu 2.0 events until they provide native 2.0 event support internally.
12+
- Mapping function sets and checks for boolean event attribute 'v2_event_mapped_into_v1',
13+
to prevent mapping from running multiple times in same pipeline.
614

715
## [2.6.0] - 2018-08-28
816
### Fixed

lib/sensu-handler.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ module Sensu
99
class Handler
1010
include Sensu::Plugin::Utils
1111
include Mixlib::CLI
12+
option :map_v2_event_into_v1,
13+
description: 'Enable 2.x to 1.4 event mapping. Alternatively set envvar SENSU_MAP_V2_EVENT_INTO_V1=1.',
14+
boolean: true,
15+
long: '--map-v2-event-into-v1'
1216

1317
attr_accessor :argv
1418

@@ -73,6 +77,14 @@ def self.disable_autorun
7377
if @@autorun
7478
handler = @@autorun.new
7579
handler.read_event(STDIN)
80+
81+
TRUTHY_VALUES = %w[1 t true yes y].freeze
82+
automap = ENV['SENSU_MAP_V2_EVENT_INTO_V1'].to_s.downcase
83+
84+
if handler.config[:map_v2_event_into_v1] || TRUTHY_VALUES.include?(automap)
85+
new_event = handler.map_v2_event_into_v1
86+
handler.event = new_event
87+
end
7688
handler.filter
7789
handler.handle
7890
end

lib/sensu-mutator.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ module Sensu
3434
class Mutator
3535
include Sensu::Plugin::Utils
3636
include Mixlib::CLI
37+
option :map_v2_event_into_v1,
38+
description: 'Enable 2.x to 1.4 event mapping. Alternatively set envvar SENSU_MAP_V2_EVENT_INTO_V1=1.',
39+
boolean: true,
40+
long: '--map-v2-event-into-v1'
3741

3842
attr_accessor :argv
3943

@@ -67,6 +71,15 @@ def self.disable_autorun
6771
return unless @@autorun
6872
mutator = @@autorun.new
6973
mutator.read_event(STDIN)
74+
75+
TRUTHY_VALUES = %w[1 t true yes y].freeze
76+
automap = ENV['SENSU_MAP_V2_EVENT_INTO_V1'].to_s.downcase
77+
78+
if mutator.config[:map_v2_event_into_v1] || TRUTHY_VALUES.include?(automap)
79+
new_event = mutator.map_v2_event_into_v1
80+
mutator.event = new_event
81+
end
82+
7083
mutator.mutate
7184
mutator.dump
7285
end

lib/sensu-plugin/utils.rb

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ def settings
2323
@settings ||= config_files.map { |f| load_config(f) }.reduce { |a, b| deep_merge(a, b) }
2424
end
2525

26+
def event
27+
@event
28+
end
29+
30+
def event=(value)
31+
@event = value
32+
end
33+
2634
def read_event(file)
2735
@event = ::JSON.parse(file.read)
2836
@event['occurrences'] ||= 1
@@ -33,6 +41,92 @@ def read_event(file)
3341
exit 0
3442
end
3543

44+
##
45+
# Helper method to convert Sensu 2.0 event into Sensu 1.4 event
46+
# This is here to help keep Sensu Plugin community handlers working
47+
# until they natively support 2.0
48+
# Takes 2.0 event json object as argument
49+
# Returns event with 1.4 mapping included
50+
#
51+
# Note:
52+
# The 1.4 mapping overwrites some attributes so the resulting event cannot
53+
# be used in a 2.0 workflow. The top level boolean attribute "v2_event_mapped_into_v1"
54+
# will be set to true as a hint to indicate this is a mapped event object.
55+
#
56+
##
57+
def map_v2_event_into_v1(orig_event = nil)
58+
orig_event ||= @event
59+
60+
# return orig_event if already mapped
61+
return orig_event if orig_event['v2_event_mapped_into_v1']
62+
63+
# Deep copy of orig_event
64+
event = Marshal.load(Marshal.dump(orig_event))
65+
66+
# Trigger mapping code if enity exists and client does not
67+
client_missing = event['client'].nil? || event['client'].empty?
68+
if event.key?('entity') && client_missing
69+
##
70+
# create the client hash from the entity hash
71+
##
72+
event['client'] = event['entity']
73+
74+
##
75+
# Fill in missing client attributes
76+
##
77+
event['client']['name'] ||= event['entity']['id']
78+
event['client']['subscribers'] ||= event['entity']['subscriptions']
79+
80+
##
81+
# Fill in renamed check attributes expected in 1.4 event
82+
# subscribers, source
83+
##
84+
event['check']['subscribers'] ||= event['check']['subscriptions']
85+
event['check']['source'] ||= event['check']['proxy_entity_id']
86+
87+
##
88+
# Mimic 1.4 event action based on 2.0 event state
89+
# action used in logs and fluentd plugins handlers
90+
##
91+
action_state_mapping = {
92+
'flapping' => 'flapping',
93+
'passing' => 'resolve',
94+
'failing' => 'create'
95+
}
96+
97+
state = event['check']['state'] || 'unknown::2.0_event'
98+
99+
# Attempt to map 2.0 event state to 1.4 event action
100+
event['action'] ||= action_state_mapping[state.downcase]
101+
# Fallback action is 2.0 event state
102+
event['action'] ||= state
103+
104+
##
105+
# Mimic 1.4 event history based on 2.0 event history
106+
# Note: This overwrites the same history attribute
107+
# 2.x history is an array of hashes, each hash includes status
108+
# 1.x history is an array of statuses
109+
##
110+
if event['check']['history']
111+
# Let's save the original history
112+
history_v2 = Marshal.load(Marshal.dump(event['check']['history']))
113+
event['check']['history_v2'] = history_v2
114+
legacy_history = []
115+
event['check']['history'].each do |h|
116+
legacy_history << h['status'].to_i.to_s || '3'
117+
end
118+
event['check']['history'] = legacy_history
119+
end
120+
121+
##
122+
# Setting flag indicating this function has already been called
123+
##
124+
event['v2_event_mapped_into_v1'] = true
125+
end
126+
# return the updated event
127+
event
128+
end
129+
36130
def net_http_req_class(method)
37131
case method.to_s.upcase
38132
when 'GET'

test/external/handle-2to1

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env ruby
2+
require 'sensu-handler'
3+
4+
class Helpers < Sensu::Handler
5+
def handle
6+
puts event_summary
7+
end
8+
9+
def api_request(*_args)
10+
nil
11+
end
12+
13+
def stash_exists?(*_args)
14+
nil
15+
end
16+
17+
def event_exists?(*_args)
18+
true
19+
end
20+
21+
def event_summary
22+
client_name = @event['client']['name']
23+
check_name = @event['check']['name']
24+
source = @event['check']['source']
25+
output = @event['check']['output']
26+
total_state_change = @event['check']['total_state_change']
27+
action = @event['action']
28+
client_subscribers = @event['client']['subscribers'].join('|')
29+
check_subscribers = @event['client']['subscribers'].join('^')
30+
history = @event['check']['history'].join('')
31+
[client_name, check_name, source, output, total_state_change, action, client_subscribers, check_subscribers, history].join(' : ')
32+
end
33+
end

test/fixtures/basic_v2_event.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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}}

test/handle_2to1_test.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
require 'test_helper'
2+
require 'English'
3+
4+
class TestHandle2to1 < MiniTest::Test
5+
include SensuPluginTestHelper
6+
7+
def setup
8+
set_script 'external/handle-2to1 --map-v2-event-into-v1'
9+
end
10+
11+
def test_2to1_enabled
12+
event = JSON.parse(fixture('basic_v2_event.json').read)
13+
expected = "test_entity : test_check : test_proxy : test_output : 4 : create : sub1|sub2|sub3 : sub1^sub2^sub3 : 01230\n"
14+
output = run_script_with_input(JSON.generate(event))
15+
assert_equal(0, $CHILD_STATUS.exitstatus)
16+
assert_match(expected, output)
17+
end
18+
end

test/mutator_2to1_test.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env ruby
2+
require 'English'
3+
4+
require 'test_helper'
5+
6+
# Simple Heper to test mutator
7+
class TestMutatorHelpers < MiniTest::Test
8+
include SensuPluginTestHelper
9+
def test_base_2to1_mutator
10+
set_script 'external/mutator-trivial --map-v2-event-into-v1'
11+
event = JSON.parse(fixture('basic_v2_event.json').read)
12+
output = run_script_with_input(JSON.generate(event))
13+
assert_equal(0, $CHILD_STATUS.exitstatus)
14+
assert_equal(event['entity']['id'], JSON.parse(output)['client']['name'])
15+
end
16+
17+
def test_external_2to1_mutator
18+
set_script 'external/mutator-helpers --map-v2-event-into-v1'
19+
event = JSON.parse(fixture('basic_v2_event.json').read)
20+
output = run_script_with_input(JSON.generate(event))
21+
assert_equal(0, $CHILD_STATUS.exitstatus)
22+
assert_equal(true, JSON.parse(output)['mutated'])
23+
end
24+
end

0 commit comments

Comments
 (0)