sensu 0.9.11 → 0.9.12.beta

Sign up to get free protection for your applications and to get access to all the features.
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
  })