sensu 0.17.0.beta → 0.17.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/sensu/api.rb DELETED
@@ -1,704 +0,0 @@
1
- require 'sensu/daemon'
2
-
3
- gem 'sinatra', '1.3.5'
4
- gem 'async_sinatra', '1.0.0'
5
-
6
- unless RUBY_PLATFORM =~ /java/
7
- gem 'thin', '1.5.0'
8
- require 'thin'
9
- end
10
-
11
- require 'sinatra/async'
12
-
13
- module Sensu
14
- class API < Sinatra::Base
15
- register Sinatra::Async
16
-
17
- class << self
18
- include Daemon
19
-
20
- def run(options={})
21
- bootstrap(options)
22
- setup_process(options)
23
- EM::run do
24
- start
25
- setup_signal_traps
26
- end
27
- end
28
-
29
- def on_reactor_run
30
- EM::next_tick do
31
- setup_redis
32
- set :redis, @redis
33
- setup_transport
34
- set :transport, @transport
35
- end
36
- end
37
-
38
- def bootstrap(options)
39
- setup_logger(options)
40
- set :logger, @logger
41
- load_settings(options)
42
- set :api, @settings[:api]
43
- set :checks, @settings[:checks]
44
- set :all_checks, @settings.checks
45
- set :cors, @settings[:cors] || {
46
- 'Origin' => '*',
47
- 'Methods' => 'GET, POST, PUT, DELETE, OPTIONS',
48
- 'Credentials' => 'true',
49
- 'Headers' => 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
50
- }
51
- on_reactor_run
52
- self
53
- end
54
-
55
- def start_server
56
- Thin::Logging.silent = true
57
- bind = @settings[:api][:bind] || '0.0.0.0'
58
- @thin = Thin::Server.new(bind, @settings[:api][:port], self)
59
- @thin.start
60
- end
61
-
62
- def stop_server(&block)
63
- @thin.stop
64
- retry_until_true do
65
- unless @thin.running?
66
- block.call
67
- true
68
- end
69
- end
70
- end
71
-
72
- def start
73
- start_server
74
- super
75
- end
76
-
77
- def stop
78
- @logger.warn('stopping')
79
- stop_server do
80
- @redis.close
81
- @transport.close
82
- super
83
- end
84
- end
85
-
86
- def test(options={})
87
- bootstrap(options)
88
- start
89
- end
90
- end
91
-
92
- configure do
93
- disable :protection
94
- disable :show_exceptions
95
- end
96
-
97
- not_found do
98
- ''
99
- end
100
-
101
- error do
102
- ''
103
- end
104
-
105
- helpers do
106
- def request_log_line
107
- settings.logger.info([env['REQUEST_METHOD'], env['REQUEST_PATH']].join(' '), {
108
- :remote_address => env['REMOTE_ADDR'],
109
- :user_agent => env['HTTP_USER_AGENT'],
110
- :request_method => env['REQUEST_METHOD'],
111
- :request_uri => env['REQUEST_URI'],
112
- :request_body => env['rack.input'].read
113
- })
114
- env['rack.input'].rewind
115
- end
116
-
117
- def protected!
118
- if settings.api[:user] && settings.api[:password]
119
- return if !(settings.api[:user] && settings.api[:password]) || authorized?
120
- headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
121
- unauthorized!
122
- end
123
- end
124
-
125
- def authorized?
126
- @auth ||= Rack::Auth::Basic::Request.new(request.env)
127
- @auth.provided? &&
128
- @auth.basic? &&
129
- @auth.credentials &&
130
- @auth.credentials == [settings.api[:user], settings.api[:password]]
131
- end
132
-
133
- def bad_request!
134
- ahalt 400
135
- end
136
-
137
- def unauthorized!
138
- throw(:halt, [401, ''])
139
- end
140
-
141
- def not_found!
142
- ahalt 404
143
- end
144
-
145
- def unavailable!
146
- ahalt 503
147
- end
148
-
149
- def created!(response)
150
- status 201
151
- body response
152
- end
153
-
154
- def accepted!(response)
155
- status 202
156
- body response
157
- end
158
-
159
- def issued!
160
- accepted!(MultiJson.dump(:issued => Time.now.to_i))
161
- end
162
-
163
- def no_content!
164
- status 204
165
- body ''
166
- end
167
-
168
- def read_data(rules={}, &block)
169
- begin
170
- data = MultiJson.load(env['rack.input'].read)
171
- valid = rules.all? do |key, rule|
172
- data[key].is_a?(rule[:type]) || (rule[:nil_ok] && data[key].nil?)
173
- end
174
- if valid
175
- block.call(data)
176
- else
177
- bad_request!
178
- end
179
- rescue MultiJson::ParseError
180
- bad_request!
181
- end
182
- end
183
-
184
- def integer_parameter(parameter)
185
- parameter =~ /^[0-9]+$/ ? parameter.to_i : nil
186
- end
187
-
188
- def pagination(items)
189
- limit = integer_parameter(params[:limit])
190
- offset = integer_parameter(params[:offset]) || 0
191
- unless limit.nil?
192
- headers['X-Pagination'] = MultiJson.dump(
193
- :limit => limit,
194
- :offset => offset,
195
- :total => items.size
196
- )
197
- paginated = items.slice(offset, limit)
198
- Array(paginated)
199
- else
200
- items
201
- end
202
- end
203
-
204
- def transport_info(&block)
205
- info = {
206
- :keepalives => {
207
- :messages => nil,
208
- :consumers => nil
209
- },
210
- :results => {
211
- :messages => nil,
212
- :consumers => nil
213
- },
214
- :connected => settings.transport.connected?
215
- }
216
- if settings.transport.connected?
217
- settings.transport.stats('keepalives') do |stats|
218
- info[:keepalives] = stats
219
- settings.transport.stats('results') do |stats|
220
- info[:results] = stats
221
- block.call(info)
222
- end
223
- end
224
- else
225
- block.call(info)
226
- end
227
- end
228
-
229
- def resolve_event(event_json)
230
- event = MultiJson.load(event_json)
231
- check = event[:check].merge(
232
- :output => 'Resolving on request of the API',
233
- :status => 0,
234
- :issued => Time.now.to_i,
235
- :executed => Time.now.to_i,
236
- :force_resolve => true
237
- )
238
- check.delete(:history)
239
- payload = {
240
- :client => event[:client][:name],
241
- :check => check
242
- }
243
- settings.logger.info('publishing check result', {
244
- :payload => payload
245
- })
246
- settings.transport.publish(:direct, 'results', MultiJson.dump(payload)) do |info|
247
- if info[:error]
248
- settings.logger.error('failed to publish check result', {
249
- :payload => payload,
250
- :error => info[:error].to_s
251
- })
252
- end
253
- end
254
- end
255
- end
256
-
257
- before do
258
- request_log_line
259
- content_type 'application/json'
260
- settings.cors.each do |header, value|
261
- headers['Access-Control-Allow-' + header] = value
262
- end
263
- protected! unless env['REQUEST_METHOD'] == 'OPTIONS'
264
- end
265
-
266
- aoptions '/*' do
267
- body ''
268
- end
269
-
270
- aget '/info/?' do
271
- transport_info do |info|
272
- response = {
273
- :sensu => {
274
- :version => VERSION
275
- },
276
- :transport => info,
277
- :redis => {
278
- :connected => settings.redis.connected?
279
- }
280
- }
281
- body MultiJson.dump(response)
282
- end
283
- end
284
-
285
- aget '/health/?' do
286
- if settings.redis.connected? && settings.transport.connected?
287
- healthy = Array.new
288
- min_consumers = integer_parameter(params[:consumers])
289
- max_messages = integer_parameter(params[:messages])
290
- transport_info do |info|
291
- if min_consumers
292
- healthy << (info[:keepalives][:consumers] >= min_consumers)
293
- healthy << (info[:results][:consumers] >= min_consumers)
294
- end
295
- if max_messages
296
- healthy << (info[:keepalives][:messages] <= max_messages)
297
- healthy << (info[:results][:messages] <= max_messages)
298
- end
299
- healthy.all? ? no_content! : unavailable!
300
- end
301
- else
302
- unavailable!
303
- end
304
- end
305
-
306
- aget '/clients/?' do
307
- response = Array.new
308
- settings.redis.smembers('clients') do |clients|
309
- clients = pagination(clients)
310
- unless clients.empty?
311
- clients.each_with_index do |client_name, index|
312
- settings.redis.get('client:' + client_name) do |client_json|
313
- response << MultiJson.load(client_json)
314
- if index == clients.size - 1
315
- body MultiJson.dump(response)
316
- end
317
- end
318
- end
319
- else
320
- body MultiJson.dump(response)
321
- end
322
- end
323
- end
324
-
325
- aget %r{/clients?/([\w\.-]+)/?$} do |client_name|
326
- settings.redis.get('client:' + client_name) do |client_json|
327
- unless client_json.nil?
328
- body client_json
329
- else
330
- not_found!
331
- end
332
- end
333
- end
334
-
335
- aget %r{/clients/([\w\.-]+)/history/?$} do |client_name|
336
- response = Array.new
337
- settings.redis.smembers('history:' + client_name) do |checks|
338
- unless checks.empty?
339
- checks.each_with_index do |check_name, index|
340
- history_key = 'history:' + client_name + ':' + check_name
341
- settings.redis.lrange(history_key, -21, -1) do |history|
342
- history.map! do |status|
343
- status.to_i
344
- end
345
- execution_key = 'execution:' + client_name + ':' + check_name
346
- settings.redis.get(execution_key) do |last_execution|
347
- unless history.empty? || last_execution.nil?
348
- item = {
349
- :check => check_name,
350
- :history => history,
351
- :last_execution => last_execution.to_i,
352
- :last_status => history.last
353
- }
354
- response << item
355
- end
356
- if index == checks.size - 1
357
- body MultiJson.dump(response)
358
- end
359
- end
360
- end
361
- end
362
- else
363
- body MultiJson.dump(response)
364
- end
365
- end
366
- end
367
-
368
- adelete %r{/clients?/([\w\.-]+)/?$} do |client_name|
369
- settings.redis.get('client:' + client_name) do |client_json|
370
- unless client_json.nil?
371
- settings.redis.hgetall('events:' + client_name) do |events|
372
- events.each do |check_name, event_json|
373
- resolve_event(event_json)
374
- end
375
- EM::Timer.new(5) do
376
- client = MultiJson.load(client_json)
377
- settings.logger.info('deleting client', {
378
- :client => client
379
- })
380
- settings.redis.srem('clients', client_name) do
381
- settings.redis.del('client:' + client_name)
382
- settings.redis.del('events:' + client_name)
383
- settings.redis.smembers('history:' + client_name) do |checks|
384
- checks.each do |check_name|
385
- settings.redis.del('history:' + client_name + ':' + check_name)
386
- settings.redis.del('execution:' + client_name + ':' + check_name)
387
- end
388
- settings.redis.del('history:' + client_name)
389
- end
390
- end
391
- end
392
- issued!
393
- end
394
- else
395
- not_found!
396
- end
397
- end
398
- end
399
-
400
- aget '/checks/?' do
401
- body MultiJson.dump(settings.all_checks)
402
- end
403
-
404
- aget %r{/checks?/([\w\.-]+)/?$} do |check_name|
405
- if settings.checks[check_name]
406
- response = settings.checks[check_name].merge(:name => check_name)
407
- body MultiJson.dump(response)
408
- else
409
- not_found!
410
- end
411
- end
412
-
413
- apost '/request/?' do
414
- rules = {
415
- :check => {:type => String, :nil_ok => false},
416
- :subscribers => {:type => Array, :nil_ok => true}
417
- }
418
- read_data(rules) do |data|
419
- if settings.checks[data[:check]]
420
- check = settings.checks[data[:check]]
421
- subscribers = data[:subscribers] || check[:subscribers] || Array.new
422
- payload = {
423
- :name => data[:check],
424
- :command => check[:command],
425
- :issued => Time.now.to_i
426
- }
427
- settings.logger.info('publishing check request', {
428
- :payload => payload,
429
- :subscribers => subscribers
430
- })
431
- subscribers.uniq.each do |exchange_name|
432
- settings.transport.publish(:fanout, exchange_name, MultiJson.dump(payload)) do |info|
433
- if info[:error]
434
- settings.logger.error('failed to publish check request', {
435
- :exchange_name => exchange_name,
436
- :payload => payload,
437
- :error => info[:error].to_s
438
- })
439
- end
440
- end
441
- end
442
- issued!
443
- else
444
- not_found!
445
- end
446
- end
447
- end
448
-
449
- aget '/events/?' do
450
- response = Array.new
451
- settings.redis.smembers('clients') do |clients|
452
- unless clients.empty?
453
- clients.each_with_index do |client_name, index|
454
- settings.redis.hgetall('events:' + client_name) do |events|
455
- events.each do |check_name, event_json|
456
- response << MultiJson.load(event_json)
457
- end
458
- if index == clients.size - 1
459
- body MultiJson.dump(response)
460
- end
461
- end
462
- end
463
- else
464
- body MultiJson.dump(response)
465
- end
466
- end
467
- end
468
-
469
- aget %r{/events/([\w\.-]+)/?$} do |client_name|
470
- response = Array.new
471
- settings.redis.hgetall('events:' + client_name) do |events|
472
- events.each do |check_name, event_json|
473
- response << MultiJson.load(event_json)
474
- end
475
- body MultiJson.dump(response)
476
- end
477
- end
478
-
479
- aget %r{/events?/([\w\.-]+)/([\w\.-]+)/?$} do |client_name, check_name|
480
- settings.redis.hgetall('events:' + client_name) do |events|
481
- event_json = events[check_name]
482
- unless event_json.nil?
483
- body event_json
484
- else
485
- not_found!
486
- end
487
- end
488
- end
489
-
490
- adelete %r{/events?/([\w\.-]+)/([\w\.-]+)/?$} do |client_name, check_name|
491
- settings.redis.hgetall('events:' + client_name) do |events|
492
- if events.include?(check_name)
493
- resolve_event(events[check_name])
494
- issued!
495
- else
496
- not_found!
497
- end
498
- end
499
- end
500
-
501
- apost '/resolve/?' do
502
- rules = {
503
- :client => {:type => String, :nil_ok => false},
504
- :check => {:type => String, :nil_ok => false}
505
- }
506
- read_data(rules) do |data|
507
- settings.redis.hgetall('events:' + data[:client]) do |events|
508
- if events.include?(data[:check])
509
- resolve_event(events[data[:check]])
510
- issued!
511
- else
512
- not_found!
513
- end
514
- end
515
- end
516
- end
517
-
518
- aget '/aggregates/?' do
519
- response = Array.new
520
- settings.redis.smembers('aggregates') do |checks|
521
- unless checks.empty?
522
- checks.each_with_index do |check_name, index|
523
- settings.redis.smembers('aggregates:' + check_name) do |aggregates|
524
- aggregates.reverse!
525
- aggregates.map! do |issued|
526
- issued.to_i
527
- end
528
- item = {
529
- :check => check_name,
530
- :issued => aggregates
531
- }
532
- response << item
533
- if index == checks.size - 1
534
- body MultiJson.dump(response)
535
- end
536
- end
537
- end
538
- else
539
- body MultiJson.dump(response)
540
- end
541
- end
542
- end
543
-
544
- aget %r{/aggregates/([\w\.-]+)/?$} do |check_name|
545
- settings.redis.smembers('aggregates:' + check_name) do |aggregates|
546
- unless aggregates.empty?
547
- aggregates.reverse!
548
- aggregates.map! do |issued|
549
- issued.to_i
550
- end
551
- age = integer_parameter(params[:age])
552
- if age
553
- timestamp = Time.now.to_i - age
554
- aggregates.reject! do |issued|
555
- issued > timestamp
556
- end
557
- end
558
- body MultiJson.dump(pagination(aggregates))
559
- else
560
- not_found!
561
- end
562
- end
563
- end
564
-
565
- adelete %r{/aggregates/([\w\.-]+)/?$} do |check_name|
566
- settings.redis.smembers('aggregates:' + check_name) do |aggregates|
567
- unless aggregates.empty?
568
- aggregates.each do |check_issued|
569
- result_set = check_name + ':' + check_issued
570
- settings.redis.del('aggregation:' + result_set)
571
- settings.redis.del('aggregate:' + result_set)
572
- end
573
- settings.redis.del('aggregates:' + check_name) do
574
- settings.redis.srem('aggregates', check_name) do
575
- no_content!
576
- end
577
- end
578
- else
579
- not_found!
580
- end
581
- end
582
- end
583
-
584
- aget %r{/aggregates?/([\w\.-]+)/([\w\.-]+)/?$} do |check_name, check_issued|
585
- result_set = check_name + ':' + check_issued
586
- settings.redis.hgetall('aggregate:' + result_set) do |aggregate|
587
- unless aggregate.empty?
588
- response = aggregate.inject(Hash.new) do |totals, (status, count)|
589
- totals[status] = Integer(count)
590
- totals
591
- end
592
- settings.redis.hgetall('aggregation:' + result_set) do |results|
593
- parsed_results = results.inject(Array.new) do |parsed, (client_name, check_json)|
594
- check = MultiJson.load(check_json)
595
- parsed << check.merge(:client => client_name)
596
- end
597
- if params[:summarize]
598
- options = params[:summarize].split(',')
599
- if options.include?('output')
600
- outputs = Hash.new(0)
601
- parsed_results.each do |result|
602
- outputs[result[:output]] += 1
603
- end
604
- response[:outputs] = outputs
605
- end
606
- end
607
- if params[:results]
608
- response[:results] = parsed_results
609
- end
610
- body MultiJson.dump(response)
611
- end
612
- else
613
- not_found!
614
- end
615
- end
616
- end
617
-
618
- apost %r{/stash(?:es)?/(.*)/?} do |path|
619
- read_data do |data|
620
- settings.redis.set('stash:' + path, MultiJson.dump(data)) do
621
- settings.redis.sadd('stashes', path) do
622
- created!(MultiJson.dump(:path => path))
623
- end
624
- end
625
- end
626
- end
627
-
628
- aget %r{/stash(?:es)?/(.*)/?} do |path|
629
- settings.redis.get('stash:' + path) do |stash_json|
630
- unless stash_json.nil?
631
- body stash_json
632
- else
633
- not_found!
634
- end
635
- end
636
- end
637
-
638
- adelete %r{/stash(?:es)?/(.*)/?} do |path|
639
- settings.redis.exists('stash:' + path) do |stash_exists|
640
- if stash_exists
641
- settings.redis.srem('stashes', path) do
642
- settings.redis.del('stash:' + path) do
643
- no_content!
644
- end
645
- end
646
- else
647
- not_found!
648
- end
649
- end
650
- end
651
-
652
- aget '/stashes/?' do
653
- response = Array.new
654
- settings.redis.smembers('stashes') do |stashes|
655
- unless stashes.empty?
656
- stashes.each_with_index do |path, index|
657
- settings.redis.get('stash:' + path) do |stash_json|
658
- settings.redis.ttl('stash:' + path) do |ttl|
659
- unless stash_json.nil?
660
- item = {
661
- :path => path,
662
- :content => MultiJson.load(stash_json),
663
- :expire => ttl
664
- }
665
- response << item
666
- else
667
- settings.redis.srem('stashes', path)
668
- end
669
- if index == stashes.size - 1
670
- body MultiJson.dump(pagination(response))
671
- end
672
- end
673
- end
674
- end
675
- else
676
- body MultiJson.dump(response)
677
- end
678
- end
679
- end
680
-
681
- apost '/stashes/?' do
682
- rules = {
683
- :path => {:type => String, :nil_ok => false},
684
- :content => {:type => Hash, :nil_ok => false},
685
- :expire => {:type => Integer, :nil_ok => true}
686
- }
687
- read_data(rules) do |data|
688
- stash_key = 'stash:' + data[:path]
689
- settings.redis.set(stash_key, MultiJson.dump(data[:content])) do
690
- settings.redis.sadd('stashes', data[:path]) do
691
- response = MultiJson.dump(:path => data[:path])
692
- if data[:expire]
693
- settings.redis.expire(stash_key, data[:expire]) do
694
- created!(response)
695
- end
696
- else
697
- created!(response)
698
- end
699
- end
700
- end
701
- end
702
- end
703
- end
704
- end