sensu 0.24.1-java → 0.25.0.beta-java

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 433bbf2acb339656f147cd33329ecf58ba53a9f7
4
- data.tar.gz: d66d5ec7ba651d64cffd1fc453e2f94c4000fa62
3
+ metadata.gz: 048445f25b420a9b822e62b06dfba2997bc88877
4
+ data.tar.gz: 2648250dabf344c8f21310fc42a68f9080305218
5
5
  SHA512:
6
- metadata.gz: f643d1332fb5b3404963f1b4cc68106cbcc3617345b03aa93efbec26a1ef293e6f51207d739e4fa5bbcaacb6d9aec164e811bbeeed28a786b502f44181d79400
7
- data.tar.gz: f2e68527b873bab0deec29975272ee6d0378d5a79bff709725cdff6f6f7c937b06590ffdb1d171bb56ce6282354679566cd6f928d1518e3658e6a83b70998b12
6
+ metadata.gz: cf83b9bc65ce7f35b98e51e2be32c8fd7e54af7f1a973f4531a5eeb6ae9b3408afe0a901279639ac841ded21a237569a1c5d8610c9f22bc678265b5066e169b9
7
+ data.tar.gz: 9c883f93e1ee1f3f7433a8e940c986b7aaf7d75b1825ea9bed5638045640686b59e7dc54eb7b4a79dcc198101da7a7d707a116fc673cc8eb5fcbfd91e6f330fe
@@ -0,0 +1,340 @@
1
+ require "sensu/api/validators"
2
+ require "sensu/api/routes"
3
+
4
+ gem "em-http-server", "0.1.8"
5
+
6
+ require "em-http-server"
7
+ require "base64"
8
+
9
+ module Sensu
10
+ module API
11
+ class HTTPHandler < EM::HttpServer::Server
12
+ include Routes
13
+
14
+ attr_accessor :logger, :settings, :redis, :transport
15
+
16
+ # Log the HTTP request. This method determines the remote
17
+ # address for the HTTP client, `@remote_address`. The debug log
18
+ # level is used for requests as response logging includes the
19
+ # same information.
20
+ def log_request
21
+ _, @remote_address = Socket.unpack_sockaddr_in(get_peername)
22
+ @logger.debug("api request", {
23
+ :remote_address => @remote_address,
24
+ :user_agent => @http[:user_agent],
25
+ :request_method => @http_request_method,
26
+ :request_uri => @http_request_uri,
27
+ :request_query_string => @http_query_string,
28
+ :request_body => @http_content
29
+ })
30
+ end
31
+
32
+ # Log the HTTP response.
33
+ def log_response
34
+ @logger.info("api response", {
35
+ :remote_address => @remote_address,
36
+ :user_agent => @http[:user_agent],
37
+ :request_method => @http_request_method,
38
+ :request_uri => @http_request_uri,
39
+ :request_query_string => @http_query_string,
40
+ :request_body => @http_content,
41
+ :response_status => @response.status,
42
+ :response_body => @response.content
43
+ })
44
+ end
45
+
46
+ # Parse the HTTP query string for parameters. This method
47
+ # creates `@params`, a hash of parsed query parameters, used by
48
+ # the API routes.
49
+ def parse_parameters
50
+ @params = {}
51
+ if @http_query_string
52
+ @http_query_string.split("&").each do |pair|
53
+ key, value = pair.split("=")
54
+ @params[key.to_sym] = value
55
+ end
56
+ end
57
+ end
58
+
59
+ # Determine if a parameter has an integer value and if so return
60
+ # it as one. This method will return `nil` if the parameter
61
+ # value is not an integer.
62
+ #
63
+ # @param value [String]
64
+ # @return [Integer, nil]
65
+ def integer_parameter(value)
66
+ value =~ /\A[0-9]+\z/ ? value.to_i : nil
67
+ end
68
+
69
+ # Read JSON data from the HTTP request content and validate it
70
+ # with the provided rules. If the HTTP request content does not
71
+ # contain valid JSON or it does not pass validation, this method
72
+ # returns a `400` (Bad Request) HTTP response.
73
+ #
74
+ # @param rules [Hash] containing the validation rules.
75
+ # @yield [Object] the callback/block called with the data after successfully
76
+ # parsing and validating the the HTTP request content.
77
+ def read_data(rules={})
78
+ begin
79
+ data = Sensu::JSON.load(@http_content)
80
+ valid = data.is_a?(Hash) && rules.all? do |key, rule|
81
+ value = data[key]
82
+ (value.is_a?(rule[:type]) || (rule[:nil_ok] && value.nil?)) &&
83
+ (value.nil? || rule[:regex].nil?) ||
84
+ (rule[:regex] && (value =~ rule[:regex]) == 0)
85
+ end
86
+ if valid
87
+ yield(data)
88
+ else
89
+ bad_request!
90
+ end
91
+ rescue Sensu::JSON::ParseError
92
+ bad_request!
93
+ end
94
+ end
95
+
96
+ # Create an EM HTTP Server HTTP response object, `@response`.
97
+ # The response object is use to build up the response status,
98
+ # status string, content type, and content. The response object
99
+ # is responsible for sending the HTTP response to the HTTP
100
+ # client and closing the connection afterwards.
101
+ #
102
+ # @return [Object]
103
+ def create_response
104
+ @response = EM::DelegatedHttpResponse.new(self)
105
+ end
106
+
107
+ # Set the cors (Cross-origin resource sharing) HTTP headers.
108
+ def set_cors_headers
109
+ api = @settings[:api]
110
+ api[:cors] ||= {
111
+ "Origin" => "*",
112
+ "Methods" => "GET, POST, PUT, DELETE, OPTIONS",
113
+ "Credentials" => "true",
114
+ "Headers" => "Origin, X-Requested-With, Content-Type, Accept, Authorization"
115
+ }
116
+ if api[:cors].is_a?(Hash)
117
+ api[:cors].each do |header, value|
118
+ @response.headers["Access-Control-Allow-#{header}"] = value
119
+ end
120
+ end
121
+ end
122
+
123
+ # Paginate the provided items. This method uses two HTTP query
124
+ # parameters to determine how to paginate the items, `limit` and
125
+ # `offset`. The parameter `limit` specifies how many items are
126
+ # to be returned in the response. The parameter `offset`
127
+ # specifies the items array index, skipping a number of items.
128
+ # This method sets the "X-Pagination" HTTP response header to a
129
+ # JSON object containing the `limit`, `offset` and `total`
130
+ # number of items that are being paginated.
131
+ #
132
+ # @param items [Array]
133
+ # @return [Array] paginated items.
134
+ def pagination(items)
135
+ limit = integer_parameter(@params[:limit])
136
+ offset = integer_parameter(@params[:offset]) || 0
137
+ unless limit.nil?
138
+ @response.headers["X-Pagination"] = Sensu::JSON.dump(
139
+ :limit => limit,
140
+ :offset => offset,
141
+ :total => items.length
142
+ )
143
+ paginated = items.slice(offset, limit)
144
+ Array(paginated)
145
+ else
146
+ items
147
+ end
148
+ end
149
+
150
+ # Respond to an HTTP request. The routes set `@response_status`,
151
+ # `@response_status_string`, and `@response_content`
152
+ # appropriately. The HTTP response status defaults to `200` with
153
+ # the status string `OK`. The Sensu API only returns JSON
154
+ # response content, `@response_content` is assumed to be a Ruby
155
+ # object that can be serialized as JSON.
156
+ def respond
157
+ @response.status = @response_status || 200
158
+ @response.status_string = @response_status_string || "OK"
159
+ if @response_content
160
+ @response.content_type "application/json"
161
+ @response.content = Sensu::JSON.dump(@response_content)
162
+ end
163
+ log_response
164
+ @response.send_response
165
+ end
166
+
167
+ # Determine if an HTTP request is authorized. This method
168
+ # compares the configured API user and password (if any) with
169
+ # the HTTP request basic authentication credentials. No
170
+ # authentication is done if the API user and password are not
171
+ # configured. OPTIONS HTTP requests bypass authentication.
172
+ #
173
+ # @return [TrueClass, FalseClass]
174
+ def authorized?
175
+ api = @settings[:api]
176
+ if api && api[:user] && api[:password]
177
+ if @http_request_method == OPTIONS_METHOD
178
+ true
179
+ elsif @http[:authorization]
180
+ scheme, base64 = @http[:authorization].split("\s")
181
+ if scheme == "Basic"
182
+ user, password = Base64.decode64(base64).split(":")
183
+ user == api[:user] && password == api[:password]
184
+ else
185
+ false
186
+ end
187
+ else
188
+ false
189
+ end
190
+ else
191
+ true
192
+ end
193
+ end
194
+
195
+ # Determine if the API is connected to Redis and the Transport.
196
+ # This method sets the `@response_content` if the API is not
197
+ # connected or it has not yet initialized the connection
198
+ # objects. The `/info` and `/health` routes are excluded from
199
+ # the connectivity checks.
200
+ def connected?
201
+ connected = true
202
+ if @redis && @transport
203
+ unless @http_request_uri =~ INFO_URI || @http_request_uri =~ HEALTH_URI
204
+ unless @redis.connected?
205
+ @response_content = {:error => "not connected to redis"}
206
+ connected = false
207
+ end
208
+ unless @transport.connected?
209
+ @response_content = {:error => "not connected to transport"}
210
+ connected = false
211
+ end
212
+ end
213
+ else
214
+ @response_content = {:error => "redis and transport connections not initialized"}
215
+ connected = false
216
+ end
217
+ connected
218
+ end
219
+
220
+ # Respond to the HTTP request with a `201` (Created) response.
221
+ def created!
222
+ @response_status = 201
223
+ @response_status_string = "Created"
224
+ respond
225
+ end
226
+
227
+ # Respond to the HTTP request with a `202` (Accepted) response.
228
+ def accepted!
229
+ @response_status = 202
230
+ @response_status_string = "Accepted"
231
+ respond
232
+ end
233
+
234
+ # Respond to the HTTP request with a `204` (No Response)
235
+ # response.
236
+ def no_content!
237
+ @response_status = 204
238
+ @response_status_string = "No Response"
239
+ respond
240
+ end
241
+
242
+ # Respond to the HTTP request with a `400` (Bad Request)
243
+ # response.
244
+ def bad_request!
245
+ @response_status = 400
246
+ @response_status_string = "Bad Request"
247
+ respond
248
+ end
249
+
250
+ # Respond to the HTTP request with a `401` (Unauthroized)
251
+ # response. This method sets the "WWW-Autenticate" HTTP response
252
+ # header.
253
+ def unauthorized!
254
+ @response.headers["WWW-Authenticate"] = 'Basic realm="Restricted Area"'
255
+ @response_status = 401
256
+ @response_status_string = "Unauthorized"
257
+ respond
258
+ end
259
+
260
+ # Respond to the HTTP request with a `404` (Not Found) response.
261
+ def not_found!
262
+ @response_status = 404
263
+ @response_status_string = "Not Found"
264
+ respond
265
+ end
266
+
267
+ # Respond to the HTTP request with a `412` (Precondition Failed)
268
+ # response.
269
+ def precondition_failed!
270
+ @response_status = 412
271
+ @response_status_string = "Precondition Failed"
272
+ respond
273
+ end
274
+
275
+ # Respond to the HTTP request with a `500` (Internal Server
276
+ # Error) response.
277
+ def error!
278
+ @response_status = 500
279
+ @response_status_string = "Internal Server Error"
280
+ respond
281
+ end
282
+
283
+ # Route the HTTP request. OPTIONS HTTP requests will always
284
+ # return a `200` with no response content. The route regular
285
+ # expressions and associated route method calls are provided by
286
+ # `ROUTES`. If a route match is not found, this method responds
287
+ # with a `404` (Not Found) HTTP response.
288
+ def route_request
289
+ if @http_request_method == OPTIONS_METHOD
290
+ respond
291
+ else
292
+ route = ROUTES[@http_request_method].detect do |route|
293
+ @http_request_uri =~ route[0]
294
+ end
295
+ unless route.nil?
296
+ send(route[1])
297
+ else
298
+ not_found!
299
+ end
300
+ end
301
+ end
302
+
303
+ # Process a HTTP request. Log the request, parse the HTTP query
304
+ # parameters, create the HTTP response object, set the cors HTTP
305
+ # response headers, determine if the request is authorized,
306
+ # determine if the API is connected to Redis and the Transport,
307
+ # and then route the HTTP request (responding to the request).
308
+ # This method is called by EM HTTP Server when handling a new
309
+ # connection.
310
+ def process_http_request
311
+ log_request
312
+ parse_parameters
313
+ create_response
314
+ set_cors_headers
315
+ if authorized?
316
+ if connected?
317
+ route_request
318
+ else
319
+ error!
320
+ end
321
+ else
322
+ unauthorized!
323
+ end
324
+ end
325
+
326
+ # Catch uncaught/unexpected errors, log them, and attempt to
327
+ # respond with a `500` (Internal Server Error) HTTP response.
328
+ # This method is called by EM HTTP Server.
329
+ #
330
+ # @param error [Object]
331
+ def http_request_errback(error)
332
+ @logger.error("unexpected api error", {
333
+ :error => error.to_s,
334
+ :backtrace => error.backtrace.join("\n")
335
+ })
336
+ error! rescue nil
337
+ end
338
+ end
339
+ end
340
+ end
@@ -1,958 +1,70 @@
1
1
  require "sensu/daemon"
2
- require "sensu/api/validators"
3
-
4
- gem "sinatra", "1.4.6"
5
- gem "async_sinatra", "1.2.0"
6
-
7
- unless RUBY_PLATFORM =~ /java/
8
- gem "thin", "1.6.4"
9
- require "thin"
10
- end
11
-
12
- require "sinatra/async"
2
+ require "sensu/api/http_handler"
13
3
 
14
4
  module Sensu
15
5
  module API
16
- class Process < Sinatra::Base
17
- register Sinatra::Async
18
-
19
- class << self
20
- include Daemon
21
-
22
- def run(options={})
23
- bootstrap(options)
24
- setup_process(options)
25
- EM::run do
26
- setup_connections
27
- start
28
- setup_signal_traps
29
- end
30
- end
31
-
32
- def setup_connections
33
- setup_redis do |redis|
34
- set :redis, redis
35
- setup_transport do |transport|
36
- set :transport, transport
37
- yield if block_given?
38
- end
39
- end
40
- end
41
-
42
- def bootstrap(options)
43
- setup_logger(options)
44
- set :logger, @logger
45
- load_settings(options)
46
- set :api, @settings[:api] || {}
47
- set :checks, @settings[:checks]
48
- set :all_checks, @settings.checks
49
- set :cors, @settings[:cors] || {
50
- "Origin" => "*",
51
- "Methods" => "GET, POST, PUT, DELETE, OPTIONS",
52
- "Credentials" => "true",
53
- "Headers" => "Origin, X-Requested-With, Content-Type, Accept, Authorization"
54
- }
55
- end
56
-
57
- def start_server
58
- Thin::Logging.silent = true
59
- bind = settings.api[:bind] || "0.0.0.0"
60
- port = settings.api[:port] || 4567
61
- @logger.info("api listening", {
62
- :bind => bind,
63
- :port => port
64
- })
65
- @thin = Thin::Server.new(bind, port, self)
66
- @thin.start
67
- end
68
-
69
- def stop_server
70
- @thin.stop
71
- retry_until_true do
72
- unless @thin.running?
73
- yield
74
- true
75
- end
76
- end
77
- end
78
-
79
- def start
80
- start_server
81
- super
82
- end
83
-
84
- def stop
85
- @logger.warn("stopping")
86
- stop_server do
87
- settings.redis.close if settings.respond_to?(:redis)
88
- settings.transport.close if settings.respond_to?(:transport)
89
- super
90
- end
91
- end
92
-
93
- def test(options={})
94
- bootstrap(options)
95
- setup_connections do
96
- start
6
+ class Process
7
+ include Daemon
8
+
9
+ # Create an instance of the Sensu API process, setup the Redis
10
+ # and Transport connections, start the API HTTP server, set up
11
+ # API process signal traps (for stopping), within the
12
+ # EventMachine event loop.
13
+ #
14
+ # @param options [Hash]
15
+ def self.run(options={})
16
+ api = self.new(options)
17
+ EM::run do
18
+ api.setup_redis
19
+ api.setup_transport
20
+ api.start
21
+ api.setup_signal_traps
22
+ end
23
+ end
24
+
25
+ # Start the Sensu API HTTP server. This method sets the service
26
+ # state to `:running`.
27
+ def start
28
+ api = @settings[:api] || {}
29
+ bind = api[:bind] || "0.0.0.0"
30
+ port = api[:port] || 4567
31
+ @logger.info("api listening", {
32
+ :bind => bind,
33
+ :port => port
34
+ })
35
+ @http_server = EM::start_server(bind, port, HTTPHandler) do |handler|
36
+ handler.logger = @logger
37
+ handler.settings = @settings
38
+ handler.redis = @redis
39
+ handler.transport = @transport
40
+ end
41
+ super
42
+ end
43
+
44
+ # Stop the Sensu API process. This method stops the HTTP server,
45
+ # closes the Redis and transport connections, sets the service
46
+ # state to `:stopped`, and stops the EventMachine event loop.
47
+ def stop
48
+ @logger.warn("stopping")
49
+ EM::stop_server(@http_server)
50
+ @redis.close if @redis
51
+ @transport.close if @transport
52
+ super
53
+ end
54
+
55
+ # Create an instance of the Sensu API with initialized
56
+ # connections for running test specs.
57
+ #
58
+ # @param options [Hash]
59
+ def self.test(options={})
60
+ api = self.new(options)
61
+ api.setup_redis do
62
+ api.setup_transport do
63
+ api.start
97
64
  yield
98
65
  end
99
66
  end
100
67
  end
101
-
102
- configure do
103
- disable :protection
104
- disable :show_exceptions
105
- end
106
-
107
- not_found do
108
- ""
109
- end
110
-
111
- error do
112
- ""
113
- end
114
-
115
- helpers do
116
- def request_log_line
117
- settings.logger.info([env["REQUEST_METHOD"], env["REQUEST_PATH"]].join(" "), {
118
- :remote_address => env["REMOTE_ADDR"],
119
- :user_agent => env["HTTP_USER_AGENT"],
120
- :request_method => env["REQUEST_METHOD"],
121
- :request_uri => env["REQUEST_URI"],
122
- :request_body => env["rack.input"].read
123
- })
124
- env["rack.input"].rewind
125
- end
126
-
127
- def connected?
128
- if settings.respond_to?(:redis) && settings.respond_to?(:transport)
129
- unless ["/info", "/health"].include?(env["REQUEST_PATH"])
130
- unless settings.redis.connected?
131
- not_connected!("not connected to redis")
132
- end
133
- unless settings.transport.connected?
134
- not_connected!("not connected to transport")
135
- end
136
- end
137
- else
138
- not_connected!("redis and transport connections not initialized")
139
- end
140
- end
141
-
142
- def authorized?
143
- @auth ||= Rack::Auth::Basic::Request.new(request.env)
144
- @auth.provided? &&
145
- @auth.basic? &&
146
- @auth.credentials &&
147
- @auth.credentials == [settings.api[:user], settings.api[:password]]
148
- end
149
-
150
- def protected!
151
- if settings.api[:user] && settings.api[:password]
152
- return if authorized?
153
- headers["WWW-Authenticate"] = 'Basic realm="Restricted Area"'
154
- unauthorized!
155
- end
156
- end
157
-
158
- def error!(body="")
159
- throw(:halt, [500, body])
160
- end
161
-
162
- def not_connected!(message)
163
- error!(Sensu::JSON.dump(:error => message))
164
- end
165
-
166
- def bad_request!
167
- ahalt 400
168
- end
169
-
170
- def unauthorized!
171
- throw(:halt, [401, ""])
172
- end
173
-
174
- def not_found!
175
- ahalt 404
176
- end
177
-
178
- def precondition_failed!
179
- ahalt 412
180
- end
181
-
182
- def created!(response)
183
- status 201
184
- body response
185
- end
186
-
187
- def accepted!(response)
188
- status 202
189
- body response
190
- end
191
-
192
- def issued!
193
- accepted!(Sensu::JSON.dump(:issued => Time.now.to_i))
194
- end
195
-
196
- def no_content!
197
- status 204
198
- body ""
199
- end
200
-
201
- def read_data(rules={})
202
- begin
203
- data = Sensu::JSON.load(env["rack.input"].read)
204
- valid = rules.all? do |key, rule|
205
- value = data[key]
206
- (value.is_a?(rule[:type]) || (rule[:nil_ok] && value.nil?)) &&
207
- (value.nil? || rule[:regex].nil?) ||
208
- (rule[:regex] && (value =~ rule[:regex]) == 0)
209
- end
210
- if valid
211
- yield(data)
212
- else
213
- bad_request!
214
- end
215
- rescue Sensu::JSON::ParseError
216
- bad_request!
217
- end
218
- end
219
-
220
- def integer_parameter(parameter)
221
- parameter =~ /\A[0-9]+\z/ ? parameter.to_i : nil
222
- end
223
-
224
- def pagination(items)
225
- limit = integer_parameter(params[:limit])
226
- offset = integer_parameter(params[:offset]) || 0
227
- unless limit.nil?
228
- headers["X-Pagination"] = Sensu::JSON.dump(
229
- :limit => limit,
230
- :offset => offset,
231
- :total => items.length
232
- )
233
- paginated = items.slice(offset, limit)
234
- Array(paginated)
235
- else
236
- items
237
- end
238
- end
239
-
240
- def transport_info
241
- info = {
242
- :keepalives => {
243
- :messages => nil,
244
- :consumers => nil
245
- },
246
- :results => {
247
- :messages => nil,
248
- :consumers => nil
249
- },
250
- :connected => settings.transport.connected?
251
- }
252
- if settings.transport.connected?
253
- settings.transport.stats("keepalives") do |stats|
254
- info[:keepalives] = stats
255
- settings.transport.stats("results") do |stats|
256
- info[:results] = stats
257
- yield(info)
258
- end
259
- end
260
- else
261
- yield(info)
262
- end
263
- end
264
-
265
- def publish_check_result(client_name, check)
266
- check[:issued] = Time.now.to_i
267
- check[:executed] = Time.now.to_i
268
- check[:status] ||= 0
269
- payload = {
270
- :client => client_name,
271
- :check => check
272
- }
273
- settings.logger.info("publishing check result", :payload => payload)
274
- settings.transport.publish(:direct, "results", Sensu::JSON.dump(payload)) do |info|
275
- if info[:error]
276
- settings.logger.error("failed to publish check result", {
277
- :payload => payload,
278
- :error => info[:error].to_s
279
- })
280
- end
281
- end
282
- end
283
-
284
- def resolve_event(event_json)
285
- event = Sensu::JSON.load(event_json)
286
- check = event[:check].merge(
287
- :output => "Resolving on request of the API",
288
- :status => 0,
289
- :force_resolve => true
290
- )
291
- check.delete(:history)
292
- publish_check_result(event[:client][:name], check)
293
- end
294
-
295
- def transport_publish_options(subscription, message)
296
- _, raw_type = subscription.split(":", 2).reverse
297
- case raw_type
298
- when "direct", "roundrobin"
299
- [:direct, subscription, message]
300
- else
301
- [:fanout, subscription, message]
302
- end
303
- end
304
-
305
- def publish_check_request(check)
306
- payload = check.merge(:issued => Time.now.to_i)
307
- settings.logger.info("publishing check request", {
308
- :payload => payload,
309
- :subscribers => check[:subscribers]
310
- })
311
- check[:subscribers].each do |subscription|
312
- options = transport_publish_options(subscription.to_s, Sensu::JSON.dump(payload))
313
- settings.transport.publish(*options) do |info|
314
- if info[:error]
315
- settings.logger.error("failed to publish check request", {
316
- :subscription => subscription,
317
- :payload => payload,
318
- :error => info[:error].to_s
319
- })
320
- end
321
- end
322
- end
323
- end
324
- end
325
-
326
- before do
327
- request_log_line
328
- content_type "application/json"
329
- settings.cors.each do |header, value|
330
- headers["Access-Control-Allow-#{header}"] = value
331
- end
332
- connected?
333
- protected! unless env["REQUEST_METHOD"] == "OPTIONS"
334
- end
335
-
336
- aoptions "/*" do
337
- body ""
338
- end
339
-
340
- aget "/info/?" do
341
- transport_info do |info|
342
- response = {
343
- :sensu => {
344
- :version => VERSION
345
- },
346
- :transport => info,
347
- :redis => {
348
- :connected => settings.redis.connected?
349
- }
350
- }
351
- body Sensu::JSON.dump(response)
352
- end
353
- end
354
-
355
- aget "/health/?" do
356
- if settings.redis.connected? && settings.transport.connected?
357
- healthy = Array.new
358
- min_consumers = integer_parameter(params[:consumers])
359
- max_messages = integer_parameter(params[:messages])
360
- transport_info do |info|
361
- if min_consumers
362
- healthy << (info[:keepalives][:consumers] >= min_consumers)
363
- healthy << (info[:results][:consumers] >= min_consumers)
364
- end
365
- if max_messages
366
- healthy << (info[:keepalives][:messages] <= max_messages)
367
- healthy << (info[:results][:messages] <= max_messages)
368
- end
369
- healthy.all? ? no_content! : precondition_failed!
370
- end
371
- else
372
- precondition_failed!
373
- end
374
- end
375
-
376
- apost "/clients/?" do
377
- read_data do |data|
378
- data[:keepalives] = data.fetch(:keepalives, false)
379
- data[:version] = VERSION
380
- data[:timestamp] = Time.now.to_i
381
- validator = Validators::Client.new
382
- if validator.valid?(data)
383
- settings.redis.set("client:#{data[:name]}", Sensu::JSON.dump(data)) do
384
- settings.redis.sadd("clients", data[:name]) do
385
- created!(Sensu::JSON.dump(:name => data[:name]))
386
- end
387
- end
388
- else
389
- bad_request!
390
- end
391
- end
392
- end
393
-
394
- aget "/clients/?" do
395
- response = Array.new
396
- settings.redis.smembers("clients") do |clients|
397
- clients = pagination(clients)
398
- unless clients.empty?
399
- clients.each_with_index do |client_name, index|
400
- settings.redis.get("client:#{client_name}") do |client_json|
401
- unless client_json.nil?
402
- response << Sensu::JSON.load(client_json)
403
- else
404
- settings.logger.error("client data missing from registry", :client_name => client_name)
405
- settings.redis.srem("clients", client_name)
406
- end
407
- if index == clients.length - 1
408
- body Sensu::JSON.dump(response)
409
- end
410
- end
411
- end
412
- else
413
- body Sensu::JSON.dump(response)
414
- end
415
- end
416
- end
417
-
418
- aget %r{^/clients?/([\w\.-]+)/?$} do |client_name|
419
- settings.redis.get("client:#{client_name}") do |client_json|
420
- unless client_json.nil?
421
- body client_json
422
- else
423
- not_found!
424
- end
425
- end
426
- end
427
-
428
- aget %r{^/clients/([\w\.-]+)/history/?$} do |client_name|
429
- response = Array.new
430
- settings.redis.smembers("result:#{client_name}") do |checks|
431
- unless checks.empty?
432
- checks.each_with_index do |check_name, index|
433
- result_key = "#{client_name}:#{check_name}"
434
- history_key = "history:#{result_key}"
435
- settings.redis.lrange(history_key, -21, -1) do |history|
436
- history.map! do |status|
437
- status.to_i
438
- end
439
- settings.redis.get("result:#{result_key}") do |result_json|
440
- unless result_json.nil?
441
- result = Sensu::JSON.load(result_json)
442
- last_execution = result[:executed]
443
- unless history.empty? || last_execution.nil?
444
- item = {
445
- :check => check_name,
446
- :history => history,
447
- :last_execution => last_execution.to_i,
448
- :last_status => history.last,
449
- :last_result => result
450
- }
451
- response << item
452
- end
453
- end
454
- if index == checks.length - 1
455
- body Sensu::JSON.dump(response)
456
- end
457
- end
458
- end
459
- end
460
- else
461
- body Sensu::JSON.dump(response)
462
- end
463
- end
464
- end
465
-
466
- adelete %r{^/clients?/([\w\.-]+)/?$} do |client_name|
467
- settings.redis.get("client:#{client_name}") do |client_json|
468
- unless client_json.nil?
469
- settings.redis.hgetall("events:#{client_name}") do |events|
470
- events.each do |check_name, event_json|
471
- resolve_event(event_json)
472
- end
473
- delete_client = Proc.new do |attempts|
474
- attempts += 1
475
- settings.redis.hgetall("events:#{client_name}") do |events|
476
- if events.empty? || attempts == 5
477
- settings.logger.info("deleting client from registry", :client_name => client_name)
478
- settings.redis.srem("clients", client_name) do
479
- settings.redis.del("client:#{client_name}")
480
- settings.redis.del("client:#{client_name}:signature")
481
- settings.redis.del("events:#{client_name}")
482
- settings.redis.smembers("result:#{client_name}") do |checks|
483
- checks.each do |check_name|
484
- result_key = "#{client_name}:#{check_name}"
485
- settings.redis.del("result:#{result_key}")
486
- settings.redis.del("history:#{result_key}")
487
- end
488
- settings.redis.del("result:#{client_name}")
489
- end
490
- end
491
- else
492
- EM::Timer.new(1) do
493
- delete_client.call(attempts)
494
- end
495
- end
496
- end
497
- end
498
- delete_client.call(0)
499
- issued!
500
- end
501
- else
502
- not_found!
503
- end
504
- end
505
- end
506
-
507
- aget "/checks/?" do
508
- body Sensu::JSON.dump(settings.all_checks)
509
- end
510
-
511
- aget %r{^/checks?/([\w\.-]+)/?$} do |check_name|
512
- if settings.checks[check_name]
513
- response = settings.checks[check_name].merge(:name => check_name)
514
- body Sensu::JSON.dump(response)
515
- else
516
- not_found!
517
- end
518
- end
519
-
520
- apost "/request/?" do
521
- rules = {
522
- :check => {:type => String, :nil_ok => false},
523
- :subscribers => {:type => Array, :nil_ok => true}
524
- }
525
- read_data(rules) do |data|
526
- if settings.checks[data[:check]]
527
- check = settings.checks[data[:check]].dup
528
- check[:name] = data[:check]
529
- check[:subscribers] ||= Array.new
530
- check[:subscribers] = data[:subscribers] if data[:subscribers]
531
- publish_check_request(check)
532
- issued!
533
- else
534
- not_found!
535
- end
536
- end
537
- end
538
-
539
- aget "/events/?" do
540
- response = Array.new
541
- settings.redis.smembers("clients") do |clients|
542
- unless clients.empty?
543
- clients.each_with_index do |client_name, index|
544
- settings.redis.hgetall("events:#{client_name}") do |events|
545
- events.each do |check_name, event_json|
546
- response << Sensu::JSON.load(event_json)
547
- end
548
- if index == clients.length - 1
549
- body Sensu::JSON.dump(response)
550
- end
551
- end
552
- end
553
- else
554
- body Sensu::JSON.dump(response)
555
- end
556
- end
557
- end
558
-
559
- aget %r{^/events/([\w\.-]+)/?$} do |client_name|
560
- response = Array.new
561
- settings.redis.hgetall("events:#{client_name}") do |events|
562
- events.each do |check_name, event_json|
563
- response << Sensu::JSON.load(event_json)
564
- end
565
- body Sensu::JSON.dump(response)
566
- end
567
- end
568
-
569
- aget %r{^/events?/([\w\.-]+)/([\w\.-]+)/?$} do |client_name, check_name|
570
- settings.redis.hgetall("events:#{client_name}") do |events|
571
- event_json = events[check_name]
572
- unless event_json.nil?
573
- body event_json
574
- else
575
- not_found!
576
- end
577
- end
578
- end
579
-
580
- adelete %r{^/events?/([\w\.-]+)/([\w\.-]+)/?$} do |client_name, check_name|
581
- settings.redis.hgetall("events:#{client_name}") do |events|
582
- if events.include?(check_name)
583
- resolve_event(events[check_name])
584
- issued!
585
- else
586
- not_found!
587
- end
588
- end
589
- end
590
-
591
- apost "/resolve/?" do
592
- rules = {
593
- :client => {:type => String, :nil_ok => false},
594
- :check => {:type => String, :nil_ok => false}
595
- }
596
- read_data(rules) do |data|
597
- settings.redis.hgetall("events:#{data[:client]}") do |events|
598
- if events.include?(data[:check])
599
- resolve_event(events[data[:check]])
600
- issued!
601
- else
602
- not_found!
603
- end
604
- end
605
- end
606
- end
607
-
608
- aget "/aggregates/?" do
609
- settings.redis.smembers("aggregates") do |aggregates|
610
- aggregates.map! do |aggregate|
611
- {:name => aggregate}
612
- end
613
- body Sensu::JSON.dump(aggregates)
614
- end
615
- end
616
-
617
- aget %r{^/aggregates/([\w\.-]+)/?$} do |aggregate|
618
- response = {
619
- :clients => 0,
620
- :checks => 0,
621
- :results => {
622
- :ok => 0,
623
- :warning => 0,
624
- :critical => 0,
625
- :unknown => 0,
626
- :total => 0,
627
- :stale => 0
628
- }
629
- }
630
- settings.redis.smembers("aggregates:#{aggregate}") do |aggregate_members|
631
- unless aggregate_members.empty?
632
- clients = []
633
- checks = []
634
- results = []
635
- aggregate_members.each_with_index do |member, index|
636
- client_name, check_name = member.split(":")
637
- clients << client_name
638
- checks << check_name
639
- result_key = "result:#{client_name}:#{check_name}"
640
- settings.redis.get(result_key) do |result_json|
641
- unless result_json.nil?
642
- results << Sensu::JSON.load(result_json)
643
- else
644
- settings.redis.srem("aggregates:#{aggregate}", member)
645
- end
646
- if index == aggregate_members.length - 1
647
- response[:clients] = clients.uniq.length
648
- response[:checks] = checks.uniq.length
649
- max_age = integer_parameter(params[:max_age])
650
- if max_age
651
- result_count = results.length
652
- timestamp = Time.now.to_i - max_age
653
- results.reject! do |result|
654
- result[:executed] < timestamp
655
- end
656
- response[:results][:stale] = result_count - results.length
657
- end
658
- response[:results][:total] = results.length
659
- results.each do |result|
660
- severity = (SEVERITIES[result[:status]] || "unknown")
661
- response[:results][severity.to_sym] += 1
662
- end
663
- body Sensu::JSON.dump(response)
664
- end
665
- end
666
- end
667
- else
668
- not_found!
669
- end
670
- end
671
- end
672
-
673
- adelete %r{^/aggregates/([\w\.-]+)/?$} do |aggregate|
674
- settings.redis.smembers("aggregates") do |aggregates|
675
- if aggregates.include?(aggregate)
676
- settings.redis.srem("aggregates", aggregate) do
677
- settings.redis.del("aggregates:#{aggregate}") do
678
- no_content!
679
- end
680
- end
681
- else
682
- not_found!
683
- end
684
- end
685
- end
686
-
687
- aget %r{^/aggregates/([\w\.-]+)/clients/?$} do |aggregate|
688
- response = Array.new
689
- settings.redis.smembers("aggregates:#{aggregate}") do |aggregate_members|
690
- unless aggregate_members.empty?
691
- clients = Hash.new
692
- aggregate_members.each do |member|
693
- client_name, check_name = member.split(":")
694
- clients[client_name] ||= []
695
- clients[client_name] << check_name
696
- end
697
- clients.each do |client_name, checks|
698
- response << {
699
- :name => client_name,
700
- :checks => checks
701
- }
702
- end
703
- body Sensu::JSON.dump(response)
704
- else
705
- not_found!
706
- end
707
- end
708
- end
709
-
710
- aget %r{^/aggregates/([\w\.-]+)/checks/?$} do |aggregate|
711
- response = Array.new
712
- settings.redis.smembers("aggregates:#{aggregate}") do |aggregate_members|
713
- unless aggregate_members.empty?
714
- checks = Hash.new
715
- aggregate_members.each do |member|
716
- client_name, check_name = member.split(":")
717
- checks[check_name] ||= []
718
- checks[check_name] << client_name
719
- end
720
- checks.each do |check_name, clients|
721
- response << {
722
- :name => check_name,
723
- :clients => clients
724
- }
725
- end
726
- body Sensu::JSON.dump(response)
727
- else
728
- not_found!
729
- end
730
- end
731
- end
732
-
733
- aget %r{^/aggregates/([\w\.-]+)/results/([\w\.-]+)/?$} do |aggregate, severity|
734
- response = Array.new
735
- if SEVERITIES.include?(severity)
736
- settings.redis.smembers("aggregates:#{aggregate}") do |aggregate_members|
737
- unless aggregate_members.empty?
738
- summaries = Hash.new
739
- max_age = integer_parameter(params[:max_age])
740
- current_timestamp = Time.now.to_i
741
- aggregate_members.each_with_index do |member, index|
742
- client_name, check_name = member.split(":")
743
- result_key = "result:#{client_name}:#{check_name}"
744
- settings.redis.get(result_key) do |result_json|
745
- unless result_json.nil?
746
- result = Sensu::JSON.load(result_json)
747
- if SEVERITIES[result[:status]] == severity &&
748
- (max_age.nil? || result[:executed] >= (current_timestamp - max_age))
749
- summaries[check_name] ||= {}
750
- summaries[check_name][result[:output]] ||= {:total => 0, :clients => []}
751
- summaries[check_name][result[:output]][:total] += 1
752
- summaries[check_name][result[:output]][:clients] << client_name
753
- end
754
- end
755
- if index == aggregate_members.length - 1
756
- summaries.each do |check_name, outputs|
757
- summary = outputs.map do |output, output_summary|
758
- {:output => output}.merge(output_summary)
759
- end
760
- response << {
761
- :check => check_name,
762
- :summary => summary
763
- }
764
- end
765
- body Sensu::JSON.dump(response)
766
- end
767
- end
768
- end
769
- else
770
- not_found!
771
- end
772
- end
773
- else
774
- bad_request!
775
- end
776
- end
777
-
778
- apost %r{^/stash(?:es)?/(.*)/?} do |path|
779
- read_data do |data|
780
- settings.redis.set("stash:#{path}", Sensu::JSON.dump(data)) do
781
- settings.redis.sadd("stashes", path) do
782
- created!(Sensu::JSON.dump(:path => path))
783
- end
784
- end
785
- end
786
- end
787
-
788
- aget %r{^/stash(?:es)?/(.*)/?} do |path|
789
- settings.redis.get("stash:#{path}") do |stash_json|
790
- unless stash_json.nil?
791
- body stash_json
792
- else
793
- not_found!
794
- end
795
- end
796
- end
797
-
798
- adelete %r{^/stash(?:es)?/(.*)/?} do |path|
799
- settings.redis.exists("stash:#{path}") do |stash_exists|
800
- if stash_exists
801
- settings.redis.srem("stashes", path) do
802
- settings.redis.del("stash:#{path}") do
803
- no_content!
804
- end
805
- end
806
- else
807
- not_found!
808
- end
809
- end
810
- end
811
-
812
- aget "/stashes/?" do
813
- response = Array.new
814
- settings.redis.smembers("stashes") do |stashes|
815
- unless stashes.empty?
816
- stashes.each_with_index do |path, index|
817
- settings.redis.get("stash:#{path}") do |stash_json|
818
- settings.redis.ttl("stash:#{path}") do |ttl|
819
- unless stash_json.nil?
820
- item = {
821
- :path => path,
822
- :content => Sensu::JSON.load(stash_json),
823
- :expire => ttl
824
- }
825
- response << item
826
- else
827
- settings.redis.srem("stashes", path)
828
- end
829
- if index == stashes.length - 1
830
- body Sensu::JSON.dump(pagination(response))
831
- end
832
- end
833
- end
834
- end
835
- else
836
- body Sensu::JSON.dump(response)
837
- end
838
- end
839
- end
840
-
841
- apost "/stashes/?" do
842
- rules = {
843
- :path => {:type => String, :nil_ok => false},
844
- :content => {:type => Hash, :nil_ok => false},
845
- :expire => {:type => Integer, :nil_ok => true}
846
- }
847
- read_data(rules) do |data|
848
- stash_key = "stash:#{data[:path]}"
849
- settings.redis.set(stash_key, Sensu::JSON.dump(data[:content])) do
850
- settings.redis.sadd("stashes", data[:path]) do
851
- response = Sensu::JSON.dump(:path => data[:path])
852
- if data[:expire]
853
- settings.redis.expire(stash_key, data[:expire]) do
854
- created!(response)
855
- end
856
- else
857
- created!(response)
858
- end
859
- end
860
- end
861
- end
862
- end
863
-
864
- apost "/results/?" do
865
- rules = {
866
- :name => {:type => String, :nil_ok => false, :regex => /\A[\w\.-]+\z/},
867
- :output => {:type => String, :nil_ok => false},
868
- :status => {:type => Integer, :nil_ok => true},
869
- :source => {:type => String, :nil_ok => true, :regex => /\A[\w\.-]+\z/}
870
- }
871
- read_data(rules) do |data|
872
- publish_check_result("sensu-api", data)
873
- issued!
874
- end
875
- end
876
-
877
- aget "/results/?" do
878
- response = Array.new
879
- settings.redis.smembers("clients") do |clients|
880
- unless clients.empty?
881
- clients.each_with_index do |client_name, client_index|
882
- settings.redis.smembers("result:#{client_name}") do |checks|
883
- if !checks.empty?
884
- checks.each_with_index do |check_name, check_index|
885
- result_key = "result:#{client_name}:#{check_name}"
886
- settings.redis.get(result_key) do |result_json|
887
- unless result_json.nil?
888
- check = Sensu::JSON.load(result_json)
889
- response << {:client => client_name, :check => check}
890
- end
891
- if client_index == clients.length - 1 && check_index == checks.length - 1
892
- body Sensu::JSON.dump(response)
893
- end
894
- end
895
- end
896
- elsif client_index == clients.length - 1
897
- body Sensu::JSON.dump(response)
898
- end
899
- end
900
- end
901
- else
902
- body Sensu::JSON.dump(response)
903
- end
904
- end
905
- end
906
-
907
- aget %r{^/results?/([\w\.-]+)/?$} do |client_name|
908
- response = Array.new
909
- settings.redis.smembers("result:#{client_name}") do |checks|
910
- unless checks.empty?
911
- checks.each_with_index do |check_name, check_index|
912
- result_key = "result:#{client_name}:#{check_name}"
913
- settings.redis.get(result_key) do |result_json|
914
- unless result_json.nil?
915
- check = Sensu::JSON.load(result_json)
916
- response << {:client => client_name, :check => check}
917
- end
918
- if check_index == checks.length - 1
919
- body Sensu::JSON.dump(response)
920
- end
921
- end
922
- end
923
- else
924
- not_found!
925
- end
926
- end
927
- end
928
-
929
- aget %r{^/results?/([\w\.-]+)/([\w\.-]+)/?$} do |client_name, check_name|
930
- result_key = "result:#{client_name}:#{check_name}"
931
- settings.redis.get(result_key) do |result_json|
932
- unless result_json.nil?
933
- check = Sensu::JSON.load(result_json)
934
- response = {:client => client_name, :check => check}
935
- body Sensu::JSON.dump(response)
936
- else
937
- not_found!
938
- end
939
- end
940
- end
941
-
942
- adelete %r{^/results?/([\w\.-]+)/([\w\.-]+)/?$} do |client_name, check_name|
943
- result_key = "result:#{client_name}:#{check_name}"
944
- settings.redis.exists(result_key) do |result_exists|
945
- if result_exists
946
- settings.redis.srem("result:#{client_name}", check_name) do
947
- settings.redis.del(result_key) do |result_json|
948
- no_content!
949
- end
950
- end
951
- else
952
- not_found!
953
- end
954
- end
955
- end
956
68
  end
957
69
  end
958
70
  end