sensu 0.24.1 → 0.25.0.beta

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1e3f468041197d7179ce425bcb860e4aaa086ed2
4
- data.tar.gz: 6cdf694c5458e4f3f2f6f4fb39f34e5fa6f6e654
3
+ metadata.gz: be31b512054dbd9dfbcaa26aafd11cafff18cb51
4
+ data.tar.gz: 3baad83fdc34d79b765e1145f8d5c44074865bd5
5
5
  SHA512:
6
- metadata.gz: 95ad9721f784e6e44d8c36f97e1f3119f1753665a3dc9bd9683600f0b241e00d637b41a51ecaf3ebc05aa2fd8c17863868e826a131db0d95b4064814ede3f970
7
- data.tar.gz: 359860c37128e467458a0a9f9ffd54261253b0c22e9bc4543b7b03f31602966176f3efcefdd420179871dba99fd0a6c0f611879c97775932919cff8a87fa923c
6
+ metadata.gz: 1ce252cc6ecfaccf54a175d61222955df5b269cf60a0dc031cd9cf1e8d9a33b74cb8ed2177bebbf24a0c8dd6e9439a5cd7a8f41d7095b70746cad7e110425f79
7
+ data.tar.gz: 7118338b715eafaec7e0fb16d1df8f8256f275b16881d9678d0243e2d63086f86dbdaae8b9abe22f318b3d382aeb3e9984959a7af078a905789c2fb213123157
@@ -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