Module: Sensu::Plugin::Utils

Included in:
Handler, Mutator
Defined in:
lib/sensu-plugin/utils.rb

Overview

rubocop:disable Metrics/ModuleLength

Instance Method Summary collapse

Instance Method Details

#api_request(method, path) {|req| ... } ⇒ Object

Yields:

  • (req)


220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/sensu-plugin/utils.rb', line 220

def api_request(method, path, &_blk)
  if api_settings.nil?
    raise 'api.json settings not found.'
  end
  use_ssl = api_settings['ssl'].is_a?(Hash) ||
            api_settings['host'].start_with?('https')
  hostname = api_settings['host'].gsub(/https?:\/\//, '')
  req = net_http_req_class(method).new(path)
  if api_settings['user'] && api_settings['password']
    req.basic_auth(api_settings['user'], api_settings['password'])
  end
  yield(req) if block_given?
  res = Net::HTTP.start(hostname, api_settings['port'], use_ssl: use_ssl) do |http|
    http.request(req)
  end
  res
end

#api_settingsHash

Return a hash of API settings derived first from ENV if set, then Sensu config ‘api` scope if configured, and finally falling back to to ipv4 localhost address on default API port.

Returns:

  • (Hash)


200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/sensu-plugin/utils.rb', line 200

def api_settings
  return @api_settings if @api_settings
  if ENV['SENSU_API_URL']
    uri = URI(ENV['SENSU_API_URL'])
    ssl = uri.scheme == 'https' ? {} : nil
    @api_settings = {
      'ssl' => ssl,
      'host' => uri.host,
      'port' => uri.port,
      'user' => uri.user,
      'password' => uri.password
    }
  else
    @api_settings = settings['api'] || {}
    @api_settings['host'] ||= '127.0.0.1'
    @api_settings['port'] ||= 4567
  end
  @api_settings
end

#api_settings=(api_settings) ⇒ Hash

Override API settings (for testing purposes)

Parameters:

  • api_settings (Hash)

Returns:

  • (Hash)


191
192
193
# File 'lib/sensu-plugin/utils.rb', line 191

def api_settings=(api_settings)
  @api_settings = api_settings
end

#cast_bool_values_int(value) ⇒ Object



281
282
283
284
285
286
287
288
289
290
# File 'lib/sensu-plugin/utils.rb', line 281

def cast_bool_values_int(value)
  case value
  when 'true', true
    1
  when 'false', false
    0
  else
    value
  end
end

#config_filesObject



8
9
10
11
12
13
14
15
16
# File 'lib/sensu-plugin/utils.rb', line 8

def config_files
  if ENV['SENSU_LOADED_TEMPFILE'] && File.file?(ENV['SENSU_LOADED_TEMPFILE'])
    IO.read(ENV['SENSU_LOADED_TEMPFILE']).split(':')
  elsif ENV['SENSU_CONFIG_FILES']
    ENV['SENSU_CONFIG_FILES'].split(':')
  else
    ['/etc/sensu/config.json'] + Dir['/etc/sensu/conf.d/**/*.json']
  end
end

#deep_merge(hash_one, hash_two) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/sensu-plugin/utils.rb', line 267

def deep_merge(hash_one, hash_two)
  merged = hash_one.dup
  hash_two.each do |key, value|
    merged[key] = if hash_one[key].is_a?(Hash) && value.is_a?(Hash)
                    deep_merge(hash_one[key], value)
                  elsif hash_one[key].is_a?(Array) && value.is_a?(Array)
                    hash_one[key].concat(value).uniq
                  else
                    value
                  end
  end
  merged
end

#eventObject



28
29
30
# File 'lib/sensu-plugin/utils.rb', line 28

def event
  @event
end

#event=(value) ⇒ Object



32
33
34
# File 'lib/sensu-plugin/utils.rb', line 32

def event=(value)
  @event = value
end

#load_config(filename) ⇒ Object



18
19
20
21
22
# File 'lib/sensu-plugin/utils.rb', line 18

def load_config(filename)
  JSON.parse(File.open(filename, 'r').read)
rescue
  {}
end

#map_go_event_into_ruby(orig_event = nil, map_annotation = nil) ⇒ Object

Helper method to convert Sensu Go event into Sensu Ruby event

This is here to help keep Sensu Plugin community handlers working
until they natively support Go events
Takes Go event json object as argument
Returns event with Sensu Ruby mapping included

Note:
  The Sensu Ruby mapping overwrites some attributes so the resulting event cannot
  be used in a Sensu Go workflow. The top level boolean attribute "go_event_mapped_into_ruby"
  will be set to true as a hint to indicate this is a mapped event object.


59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/sensu-plugin/utils.rb', line 59

def map_go_event_into_ruby(orig_event = nil, map_annotation = nil)
  orig_event ||= @event

  map_annotation ||= ENV['SENSU_MAP_ANNOTATION'] if ENV['SENSU_MAP_ANNOTATION']
  map_annotation ||= 'sensu.io.json_attributes'

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

  # 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']['subscribers'] ||= event['entity']['subscriptions']

    ##
    #  Map entity metadata into client attributes
    #  Note this is potentially destructive as it may overwrite existing client attributes.
    ##
    if event['entity'].key?('metadata')
      ##
      #  Map metadata annotation 'name' to client name attribute
      ##
      event['client']['name'] ||= event['entity']['metadata']['name']

      ##
      #  Map special metadata annotation defined in map_annotation as json string and convert to client attributes
      #  Note this is potentially destructive as it may overwrite existing client attributes.
      ##
      if event['entity']['metadata'].key?('annotations') && event['entity']['metadata']['annotations'].key?(map_annotation)
        json_hash = JSON.parse(event['entity']['metadata']['annotations'][map_annotation])
        event['client'].update(json_hash)
      end
    end

    ##
    # Fill in renamed check attributes expected in Sensu Ruby event
    #   subscribers, source
    ##
    event['check']['subscribers'] ||= event['check']['subscriptions']
    event['check']['source'] ||= event['check']['proxy_entity_name']

    ##
    # Mimic Sensu Ruby event action based on Go event state
    #  action used in logs and fluentd plugins handlers
    ##
    action_state_mapping = {
      'flapping' => 'flapping',
      'passing' => 'resolve',
      'failing' => 'create'
    }

    state = event['check']['state'] || 'unknown::go_event'

    # Attempt to map Go event state to Sensu Ruby event action
    event['action'] ||= action_state_mapping[state.downcase]
    # Fallback action is Go event state
    event['action'] ||= state

    ##
    # Mimic Sensu Ruby event history based on Go event history
    #  Note: This overwrites the same history attribute
    #    Go history is an array of hashes, each hash includes status
    #    Sensu Ruby history is an array of statuses
    ##
    if event['check']['history']
      # Let's save the original history
      original_history = Marshal.load(Marshal.dump(event['check']['history']))
      event['check']['original_history'] = original_history
      legacy_history = []
      event['check']['history'].each do |h|
        legacy_history << h['status'].to_i.to_s || '3'
      end
      event['check']['history'] = legacy_history
    end

    ##
    #  Map check metadata into client attributes
    #  Note this is potentially destructive as it may overwrite existing check attributes.
    ##
    if event['check'].key?('metadata')
      ##
      #  Map metadata annotation 'name' to client name attribute
      ##
      event['check']['name'] ||= event['check']['metadata']['name']

      ##
      #  Map special metadata annotation defined in map_annotation as json string and convert to check attributes
      #  Note this is potentially destructive as it may overwrite existing check attributes.
      ##
      if event['check']['metadata'].key?('annotations') && event['check']['metadata']['annotations'].key?(map_annotation)
        json_hash = JSON.parse(event['check']['metadata']['annotations'][map_annotation])
        event['check'].update(json_hash)
      end
    end
    ##
    # Setting flag indicating this function has already been called
    ##
    event['go_event_mapped_into_ruby'] = true
  end
  # return the updated event
  event
end

#net_http_req_class(method) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/sensu-plugin/utils.rb', line 174

def net_http_req_class(method)
  case method.to_s.upcase
  when 'GET'
    Net::HTTP::Get
  when 'POST'
    Net::HTTP::Post
  when 'DELETE'
    Net::HTTP::Delete
  when 'PUT'
    Net::HTTP::Put
  end
end

#paginated_get(path, options = {}) ⇒ Array

Use API query parameters to paginate HTTP GET requests, iterating over the results until an empty set is returned.

Parameters:

  • path (String)
  • options (Hash) (defaults to: {})

Returns:

  • (Array)


245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# File 'lib/sensu-plugin/utils.rb', line 245

def paginated_get(path, options = {})
  limit = options.fetch('limit', 500)
  offset = 0
  results = []
  loop do
    query_path = "#{path}?limit=#{limit}&offset=#{offset}"
    response = api_request(:GET, query_path)
    unless response.is_a?(Net::HTTPOK)
      unknown("Non-OK response from API query: #{get_uri(query_path)}")
    end
    data = JSON.parse(response.body)
    # when the data is empty, we have hit the end
    break if data.empty?
    # If API lacks pagination support, it will
    # return same data on subsequent iterations
    break if results.any? { |r| r == data }
    results << data
    offset += limit
  end
  results.flatten
end

#read_event(file) ⇒ Object



36
37
38
39
40
41
42
43
44
# File 'lib/sensu-plugin/utils.rb', line 36

def read_event(file)
  @event = ::JSON.parse(file.read)
  @event['occurrences'] ||= 1
  @event['check']       ||= {}
  @event['client']      ||= {}
rescue => e
  puts 'error reading event: ' + e.message
  exit 0
end

#settingsObject



24
25
26
# File 'lib/sensu-plugin/utils.rb', line 24

def settings
  @settings ||= config_files.map { |f| load_config(f) }.reduce { |a, b| deep_merge(a, b) }
end