sensu 0.9.11 → 0.9.12.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## 0.9.12 - TBD
2
+
3
+ ### Features
4
+
5
+ Check extensions.
6
+
7
+ ### Non-backwards compatible changes
8
+
9
+ No longer defaults to or enforces a specific config file or directory.
10
+
11
+ ### Other
12
+
13
+ Running on Ruby 2.0.0p0.
14
+
15
+ Faster JSON parser.
16
+
17
+ Lightweight logger.
18
+
19
+ Improved config validation.
20
+
1
21
  ## 0.9.11 - 2013-02-22
2
22
 
3
23
  ### Features
data/lib/sensu/api.rb CHANGED
@@ -87,8 +87,15 @@ module Sensu
87
87
  end
88
88
 
89
89
  def trap_signals
90
- %w[INT TERM].each do |signal|
90
+ $signals = Array.new
91
+ STOP_SIGNALS.each do |signal|
91
92
  Signal.trap(signal) do
93
+ $signals << signal
94
+ end
95
+ end
96
+ EM::PeriodicTimer.new(1) do
97
+ signal = $signals.shift
98
+ if STOP_SIGNALS.include?(signal)
92
99
  $logger.warn('received signal', {
93
100
  :signal => signal
94
101
  })
@@ -152,7 +159,7 @@ module Sensu
152
159
  end
153
160
 
154
161
  def event_hash(event_json, client_name, check_name)
155
- JSON.parse(event_json, :symbolize_names => true).merge(
162
+ Oj.load(event_json).merge(
156
163
  :client => client_name,
157
164
  :check => check_name
158
165
  )
@@ -173,7 +180,7 @@ module Sensu
173
180
  $logger.info('publishing check result', {
174
181
  :payload => payload
175
182
  })
176
- $amq.direct('results').publish(payload.to_json)
183
+ $amq.direct('results').publish(Oj.dump(payload))
177
184
  end
178
185
  end
179
186
 
@@ -209,11 +216,11 @@ module Sensu
209
216
  $amq.queue('results').status do |messages, consumers|
210
217
  response[:rabbitmq][:results][:messages] = messages
211
218
  response[:rabbitmq][:results][:consumers] = consumers
212
- body response.to_json
219
+ body Oj.dump(response)
213
220
  end
214
221
  end
215
222
  else
216
- body response.to_json
223
+ body Oj.dump(response)
217
224
  end
218
225
  end
219
226
 
@@ -223,14 +230,14 @@ module Sensu
223
230
  unless clients.empty?
224
231
  clients.each_with_index do |client_name, index|
225
232
  $redis.get('client:' + client_name) do |client_json|
226
- response.push(JSON.parse(client_json))
233
+ response << Oj.load(client_json)
227
234
  if index == clients.size - 1
228
- body response.to_json
235
+ body Oj.dump(response)
229
236
  end
230
237
  end
231
238
  end
232
239
  else
233
- body response.to_json
240
+ body Oj.dump(response)
234
241
  end
235
242
  end
236
243
  end
@@ -253,7 +260,7 @@ module Sensu
253
260
  resolve_event(event_hash(event_json, client_name, check_name))
254
261
  end
255
262
  EM::Timer.new(5) do
256
- client = JSON.parse(client_json, :symbolize_names => true)
263
+ client = Oj.load(client_json)
257
264
  $logger.info('deleting client', {
258
265
  :client => client
259
266
  })
@@ -276,13 +283,13 @@ module Sensu
276
283
  end
277
284
 
278
285
  aget '/checks' do
279
- body $settings.checks.to_json
286
+ body Oj.dump($settings.checks)
280
287
  end
281
288
 
282
289
  aget %r{/checks?/([\w\.-]+)$} do |check_name|
283
290
  if $settings.check_exists?(check_name)
284
291
  response = $settings[:checks][check_name].merge(:name => check_name)
285
- body response.to_json
292
+ body Oj.dump(response)
286
293
  else
287
294
  not_found!
288
295
  end
@@ -290,7 +297,7 @@ module Sensu
290
297
 
291
298
  apost %r{/(?:check/)?request$} do
292
299
  begin
293
- post_body = JSON.parse(request.body.read, :symbolize_names => true)
300
+ post_body = Oj.load(request.body.read)
294
301
  check_name = post_body[:check]
295
302
  subscribers = post_body[:subscribers] || Array.new
296
303
  if check_name.is_a?(String) && subscribers.is_a?(Array)
@@ -309,7 +316,7 @@ module Sensu
309
316
  :subscribers => subscribers
310
317
  })
311
318
  subscribers.uniq.each do |exchange_name|
312
- $amq.fanout(exchange_name).publish(payload.to_json)
319
+ $amq.fanout(exchange_name).publish(Oj.dump(payload))
313
320
  end
314
321
  created!
315
322
  else
@@ -318,7 +325,7 @@ module Sensu
318
325
  else
319
326
  bad_request!
320
327
  end
321
- rescue JSON::ParserError, TypeError
328
+ rescue Oj::ParseError, TypeError
322
329
  bad_request!
323
330
  end
324
331
  end
@@ -330,15 +337,15 @@ module Sensu
330
337
  clients.each_with_index do |client_name, index|
331
338
  $redis.hgetall('events:' + client_name) do |events|
332
339
  events.each do |check_name, event_json|
333
- response.push(event_hash(event_json, client_name, check_name))
340
+ response << event_hash(event_json, client_name, check_name)
334
341
  end
335
342
  if index == clients.size - 1
336
- body response.to_json
343
+ body Oj.dump(response)
337
344
  end
338
345
  end
339
346
  end
340
347
  else
341
- body response.to_json
348
+ body Oj.dump(response)
342
349
  end
343
350
  end
344
351
  end
@@ -347,9 +354,9 @@ module Sensu
347
354
  response = Array.new
348
355
  $redis.hgetall('events:' + client_name) do |events|
349
356
  events.each do |check_name, event_json|
350
- response.push(event_hash(event_json, client_name, check_name))
357
+ response << event_hash(event_json, client_name, check_name)
351
358
  end
352
- body response.to_json
359
+ body Oj.dump(response)
353
360
  end
354
361
  end
355
362
 
@@ -357,7 +364,7 @@ module Sensu
357
364
  $redis.hgetall('events:' + client_name) do |events|
358
365
  event_json = events[check_name]
359
366
  unless event_json.nil?
360
- body event_hash(event_json, client_name, check_name).to_json
367
+ body Oj.dump(event_hash(event_json, client_name, check_name))
361
368
  else
362
369
  not_found!
363
370
  end
@@ -377,7 +384,7 @@ module Sensu
377
384
 
378
385
  apost %r{/(?:event/)?resolve$} do
379
386
  begin
380
- post_body = JSON.parse(request.body.read, :symbolize_names => true)
387
+ post_body = Oj.load(request.body.read)
381
388
  client_name = post_body[:client]
382
389
  check_name = post_body[:check]
383
390
  if client_name.is_a?(String) && check_name.is_a?(String)
@@ -392,7 +399,7 @@ module Sensu
392
399
  else
393
400
  bad_request!
394
401
  end
395
- rescue JSON::ParserError, TypeError
402
+ rescue Oj::ParseError, TypeError
396
403
  bad_request!
397
404
  end
398
405
  end
@@ -407,14 +414,14 @@ module Sensu
407
414
  :check => check_name,
408
415
  :issued => aggregates.sort.reverse
409
416
  }
410
- response.push(collection)
417
+ response << collection
411
418
  if index == checks.size - 1
412
- body response.to_json
419
+ body Oj.dump(response)
413
420
  end
414
421
  end
415
422
  end
416
423
  else
417
- body response.to_json
424
+ body Oj.dump(response)
418
425
  end
419
426
  end
420
427
  end
@@ -442,7 +449,7 @@ module Sensu
442
449
  end
443
450
  end
444
451
  if valid_request
445
- body aggregates.sort.reverse.take(limit).to_json
452
+ body Oj.dump(aggregates.sort.reverse.take(limit))
446
453
  else
447
454
  bad_request!
448
455
  end
@@ -481,8 +488,8 @@ module Sensu
481
488
  end
482
489
  $redis.hgetall('aggregation:' + result_set) do |results|
483
490
  parsed_results = results.inject(Array.new) do |parsed, (client_name, check_json)|
484
- check = JSON.parse(check_json, :symbolize_names => true)
485
- parsed.push(check.merge(:client => client_name))
491
+ check = Oj.load(check_json)
492
+ parsed << check.merge(:client => client_name)
486
493
  end
487
494
  if params[:summarize]
488
495
  options = params[:summarize].split(',')
@@ -497,7 +504,7 @@ module Sensu
497
504
  if params[:results]
498
505
  response[:results] = parsed_results
499
506
  end
500
- body response.to_json
507
+ body Oj.dump(response)
501
508
  end
502
509
  else
503
510
  not_found!
@@ -507,13 +514,13 @@ module Sensu
507
514
 
508
515
  apost %r{/stash(?:es)?/(.*)} do |path|
509
516
  begin
510
- post_body = JSON.parse(request.body.read)
511
- $redis.set('stash:' + path, post_body.to_json) do
517
+ post_body = Oj.load(request.body.read)
518
+ $redis.set('stash:' + path, Oj.dump(post_body)) do
512
519
  $redis.sadd('stashes', path) do
513
520
  created!
514
521
  end
515
522
  end
516
- rescue JSON::ParserError
523
+ rescue Oj::ParseError
517
524
  bad_request!
518
525
  end
519
526
  end
@@ -544,29 +551,29 @@ module Sensu
544
551
 
545
552
  aget '/stashes' do
546
553
  $redis.smembers('stashes') do |stashes|
547
- body stashes.to_json
554
+ body Oj.dump(stashes)
548
555
  end
549
556
  end
550
557
 
551
558
  apost '/stashes' do
552
559
  begin
553
- post_body = JSON.parse(request.body.read)
560
+ post_body = Oj.load(request.body.read)
554
561
  if post_body.is_a?(Array) && post_body.size > 0
555
562
  response = Hash.new
556
563
  post_body.each_with_index do |path, index|
557
564
  $redis.get('stash:' + path) do |stash_json|
558
565
  unless stash_json.nil?
559
- response[path] = JSON.parse(stash_json)
566
+ response[path] = Oj.load(stash_json)
560
567
  end
561
568
  if index == post_body.size - 1
562
- body response.to_json
569
+ body Oj.dump(response)
563
570
  end
564
571
  end
565
572
  end
566
573
  else
567
574
  bad_request!
568
575
  end
569
- rescue JSON::ParserError
576
+ rescue Oj::ParseError
570
577
  bad_request!
571
578
  end
572
579
  end
data/lib/sensu/base.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  require 'rubygems'
2
2
 
3
- gem 'eventmachine', '1.0.0'
3
+ gem 'oj', '2.0.8'
4
+ gem 'eventmachine', '1.0.1'
4
5
 
5
- require 'json'
6
6
  require 'time'
7
7
  require 'uri'
8
+ require 'oj'
8
9
 
9
10
  require File.join(File.dirname(__FILE__), 'constants')
10
11
  require File.join(File.dirname(__FILE__), 'utilities')
@@ -16,27 +17,35 @@ require File.join(File.dirname(__FILE__), 'process')
16
17
  require File.join(File.dirname(__FILE__), 'io')
17
18
  require File.join(File.dirname(__FILE__), 'rabbitmq')
18
19
 
20
+ Oj.default_options = {:symbol_keys => true}
21
+
19
22
  module Sensu
20
23
  class Base
21
24
  def initialize(options={})
22
- @options = DEFAULT_OPTIONS.merge(options)
25
+ @options = options
23
26
  end
24
27
 
25
28
  def logger
26
- stream = LogStream.new
27
- stream.level = @options[:log_level]
29
+ logger = Logger.get
30
+ if @options[:log_level]
31
+ logger.level = @options[:log_level]
32
+ end
28
33
  if @options[:log_file]
29
- stream.reopen(@options[:log_file])
34
+ logger.reopen(@options[:log_file])
30
35
  end
31
- stream.setup_traps
32
- stream.logger
36
+ logger.setup_traps
37
+ logger
33
38
  end
34
39
 
35
40
  def settings
36
41
  settings = Settings.new
37
42
  settings.load_env
38
- settings.load_file(@options[:config_file])
39
- settings.load_directory(@options[:config_dir])
43
+ if @options[:config_file]
44
+ settings.load_file(@options[:config_file])
45
+ end
46
+ if @options[:config_dir]
47
+ settings.load_directory(@options[:config_dir])
48
+ end
40
49
  settings.validate
41
50
  settings.set_env
42
51
  settings
data/lib/sensu/cli.rb CHANGED
@@ -13,13 +13,13 @@ module Sensu
13
13
  puts VERSION
14
14
  exit
15
15
  end
16
- opts.on('-c', '--config FILE', 'Sensu JSON config FILE. Default: /etc/sensu/config.json') do |file|
16
+ opts.on('-c', '--config FILE', 'Sensu JSON config FILE') do |file|
17
17
  options[:config_file] = file
18
18
  end
19
- opts.on('-d', '--config_dir DIR', 'DIR for supplemental Sensu JSON config files. Default: /etc/sensu/conf.d/') do |dir|
19
+ opts.on('-d', '--config_dir DIR', 'DIR for Sensu JSON config files') do |dir|
20
20
  options[:config_dir] = dir
21
21
  end
22
- opts.on('-e', '--extension_dir DIR', 'DIR for Sensu extensions (experimental)') do |dir|
22
+ opts.on('-e', '--extension_dir DIR', 'DIR for Sensu extensions') do |dir|
23
23
  options[:extension_dir] = dir
24
24
  end
25
25
  opts.on('-l', '--log FILE', 'Log to a given FILE. Default: STDOUT') do |file|
@@ -36,7 +36,7 @@ module Sensu
36
36
  end
37
37
  end
38
38
  optparse.parse!(arguments)
39
- DEFAULT_OPTIONS.merge(options)
39
+ options
40
40
  end
41
41
  end
42
42
  end
data/lib/sensu/client.rb CHANGED
@@ -19,6 +19,7 @@ module Sensu
19
19
  base = Base.new(options)
20
20
  @logger = base.logger
21
21
  @settings = base.settings
22
+ @extensions = base.extensions
22
23
  base.setup_process
23
24
  @timers = Array.new
24
25
  @checks_in_progress = Array.new
@@ -50,7 +51,7 @@ module Sensu
50
51
  @logger.debug('publishing keepalive', {
51
52
  :payload => payload
52
53
  })
53
- @amq.direct('keepalives').publish(payload.to_json)
54
+ @amq.direct('keepalives').publish(Oj.dump(payload))
54
55
  end
55
56
 
56
57
  def setup_keepalives
@@ -71,38 +72,40 @@ module Sensu
71
72
  @logger.info('publishing check result', {
72
73
  :payload => payload
73
74
  })
74
- @amq.direct('results').publish(payload.to_json)
75
+ @amq.direct('results').publish(Oj.dump(payload))
75
76
  end
76
77
 
77
- def execute_check(check)
78
- @logger.debug('attempting to execute check', {
78
+ def substitute_command_tokens(check)
79
+ unmatched_tokens = Array.new
80
+ substituted = check[:command].gsub(/:::(.*?):::/) do
81
+ token = $1.to_s
82
+ matched = token.split('.').inject(@settings[:client]) do |client, attribute|
83
+ client[attribute].nil? ? break : client[attribute]
84
+ end
85
+ if matched.nil?
86
+ unmatched_tokens << token
87
+ end
88
+ matched
89
+ end
90
+ [substituted, unmatched_tokens]
91
+ end
92
+
93
+ def execute_check_command(check)
94
+ @logger.debug('attempting to execute check command', {
79
95
  :check => check
80
96
  })
81
97
  unless @checks_in_progress.include?(check[:name])
82
- @logger.debug('executing check', {
83
- :check => check
84
- })
85
- @checks_in_progress.push(check[:name])
86
- unmatched_tokens = Array.new
87
- command = check[:command].gsub(/:::(.*?):::/) do
88
- token = $1.to_s
89
- matched = token.split('.').inject(@settings[:client]) do |client, attribute|
90
- client[attribute].nil? ? break : client[attribute]
91
- end
92
- if matched.nil?
93
- unmatched_tokens.push(token)
94
- end
95
- matched
96
- end
98
+ @checks_in_progress << check[:name]
99
+ command, unmatched_tokens = substitute_command_tokens(check)
97
100
  if unmatched_tokens.empty?
98
101
  execute = Proc.new do
102
+ @logger.debug('executing check command', {
103
+ :check => check
104
+ })
99
105
  started = Time.now.to_f
100
106
  begin
101
107
  check[:output], check[:status] = IO.popen(command, 'r', check[:timeout])
102
108
  rescue => error
103
- @logger.warn('unexpected error', {
104
- :error => error.to_s
105
- })
106
109
  check[:output] = 'Unexpected error: ' + error.to_s
107
110
  check[:status] = 2
108
111
  end
@@ -115,58 +118,76 @@ module Sensu
115
118
  end
116
119
  EM::defer(execute, publish)
117
120
  else
118
- @logger.warn('missing client attributes', {
119
- :check => check,
120
- :unmatched_tokens => unmatched_tokens
121
- })
122
- check[:output] = 'Missing client attributes: ' + unmatched_tokens.join(', ')
121
+ check[:output] = 'Unmatched command tokens: ' + unmatched_tokens.join(', ')
123
122
  check[:status] = 3
124
123
  check[:handle] = false
125
124
  publish_result(check)
126
125
  @checks_in_progress.delete(check[:name])
127
126
  end
128
127
  else
129
- @logger.warn('previous check execution in progress', {
128
+ @logger.warn('previous check command execution in progress', {
130
129
  :check => check
131
130
  })
132
131
  end
133
132
  end
134
133
 
134
+ def run_check_extension(check)
135
+ @logger.debug('attempting to run check extension', {
136
+ :check => check
137
+ })
138
+ extension = @extensions[:checks][check[:name]]
139
+ extension.run do |output, status|
140
+ check[:output] = output
141
+ check[:status] = status
142
+ publish_result(check)
143
+ end
144
+ end
145
+
146
+ def process_check(check)
147
+ @logger.debug('processing check', {
148
+ :check => check
149
+ })
150
+ if check.has_key?(:command)
151
+ if @settings.check_exists?(check[:name])
152
+ check.merge!(@settings[:checks][check[:name]])
153
+ execute_check_command(check)
154
+ elsif @safe_mode
155
+ check[:output] = 'Check is not locally defined (safe mode)'
156
+ check[:status] = 3
157
+ check[:handle] = false
158
+ publish_result(check)
159
+ else
160
+ execute_check_command(check)
161
+ end
162
+ else
163
+ if @extensions.check_exists?(check[:name])
164
+ run_check_extension(check)
165
+ else
166
+ @logger.warn('unknown check extension', {
167
+ :check => check
168
+ })
169
+ end
170
+ end
171
+ end
172
+
135
173
  def setup_subscriptions
136
174
  @logger.debug('subscribing to client subscriptions')
137
175
  @check_request_queue = @amq.queue('', :auto_delete => true) do |queue|
138
- @settings[:client][:subscriptions].uniq.each do |exchange_name|
176
+ @settings[:client][:subscriptions].each do |exchange_name|
139
177
  @logger.debug('binding queue to exchange', {
140
- :queue => {
141
- :name => queue.name
142
- },
143
- :exchange => {
144
- :name => exchange_name
145
- }
178
+ :queue_name => queue.name,
179
+ :exchange_name => exchange_name
146
180
  })
147
181
  queue.bind(@amq.fanout(exchange_name))
148
182
  end
149
183
  queue.subscribe do |payload|
150
184
  begin
151
- check = JSON.parse(payload, :symbolize_names => true)
185
+ check = Oj.load(payload)
152
186
  @logger.info('received check request', {
153
187
  :check => check
154
188
  })
155
- if @settings.check_exists?(check[:name])
156
- check.merge!(@settings[:checks][check[:name]])
157
- execute_check(check)
158
- elsif @safe_mode
159
- @logger.warn('check is not defined', {
160
- :check => check
161
- })
162
- check[:output] = 'Check is not defined (safe mode)'
163
- check[:status] = 3
164
- check[:handle] = false
165
- publish_result(check)
166
- else
167
- execute_check(check)
168
- end
169
- rescue JSON::ParserError => error
189
+ process_check(check)
190
+ rescue Oj::ParseError => error
170
191
  @logger.warn('check request payload must be valid json', {
171
192
  :payload => payload,
172
193
  :error => error.to_s
@@ -176,41 +197,48 @@ module Sensu
176
197
  end
177
198
  end
178
199
 
179
- def setup_standalone
180
- @logger.debug('scheduling standalone checks')
181
- standalone_check_count = 0
200
+ def schedule_checks(checks)
201
+ check_count = 0
182
202
  stagger = testing? ? 0 : 2
183
- @settings.checks.each do |check|
184
- if check[:standalone]
185
- standalone_check_count += 1
186
- scheduling_delay = stagger * standalone_check_count % 30
187
- @timers << EM::Timer.new(scheduling_delay) do
188
- interval = testing? ? 0.5 : check[:interval]
189
- @timers << EM::PeriodicTimer.new(interval) do
190
- if @rabbitmq.connected?
191
- check[:issued] = Time.now.to_i
192
- execute_check(check)
193
- end
203
+ checks.each do |check|
204
+ check_count += 1
205
+ scheduling_delay = stagger * check_count % 30
206
+ @timers << EM::Timer.new(scheduling_delay) do
207
+ interval = testing? ? 0.5 : check[:interval]
208
+ @timers << EM::PeriodicTimer.new(interval) do
209
+ if @rabbitmq.connected?
210
+ check[:issued] = Time.now.to_i
211
+ process_check(check)
194
212
  end
195
213
  end
196
214
  end
197
215
  end
198
216
  end
199
217
 
218
+ def setup_standalone
219
+ @logger.debug('scheduling standalone checks')
220
+ standard_checks = @settings.checks.select do |check|
221
+ check[:standalone]
222
+ end
223
+ extension_checks = @extensions.checks.select do |check|
224
+ check[:standalone] && check[:interval].is_a?(Integer)
225
+ end
226
+ schedule_checks(standard_checks + extension_checks)
227
+ end
228
+
200
229
  def setup_sockets
201
230
  @logger.debug('binding client tcp socket')
202
231
  EM::start_server('127.0.0.1', 3030, Socket) do |socket|
203
- socket.protocol = :tcp
204
232
  socket.logger = @logger
205
233
  socket.settings = @settings
206
234
  socket.amq = @amq
207
235
  end
208
236
  @logger.debug('binding client udp socket')
209
237
  EM::open_datagram_socket('127.0.0.1', 3030, Socket) do |socket|
210
- socket.protocol = :udp
211
238
  socket.logger = @logger
212
239
  socket.settings = @settings
213
240
  socket.amq = @amq
241
+ socket.reply = false
214
242
  end
215
243
  end
216
244
 
@@ -255,8 +283,15 @@ module Sensu
255
283
  end
256
284
 
257
285
  def trap_signals
258
- %w[INT TERM].each do |signal|
286
+ @signals = Array.new
287
+ STOP_SIGNALS.each do |signal|
259
288
  Signal.trap(signal) do
289
+ @signals << signal
290
+ end
291
+ end
292
+ EM::PeriodicTimer.new(1) do
293
+ signal = @signals.shift
294
+ if STOP_SIGNALS.include?(signal)
260
295
  @logger.warn('received signal', {
261
296
  :signal => signal
262
297
  })