-
Notifications
You must be signed in to change notification settings - Fork 117
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
Changes from 23 commits
feb1b95
d0d531e
339ead8
06dcf07
01fb9e1
c165f1e
bfa7433
009fb83
65cc0d7
92cb685
ac18b2e
c0f891b
40ba5b8
953b1d2
68152d2
5be70d9
a160133
fd160df
2a39832
6203e84
f4a7d68
a6d8b07
b951c11
acb27c3
0af1f41
7ef0723
38d5a97
21fca96
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
||
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks like an extra There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. 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' | ||
|
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 |
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}} |
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 |
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 |
There was a problem hiding this comment.
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.