sensu 1.0.4 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -0
- data/README.md +4 -4
- data/lib/sensu/api/http_handler.rb +2 -20
- data/lib/sensu/api/routes/results.rb +3 -1
- data/lib/sensu/client/http_socket.rb +20 -13
- data/lib/sensu/client/process.rb +83 -8
- data/lib/sensu/constants.rb +1 -1
- data/lib/sensu/daemon.rb +1 -1
- data/lib/sensu/server/handle.rb +2 -1
- data/lib/sensu/server/process.rb +187 -63
- data/sensu.gemspec +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed1314dc2d888635865717c37c4e71d77da17b28
|
4
|
+
data.tar.gz: 5f90de786a5691adf798879beca63a4d11732f38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ad6541d3bba0982e9b0f397349f29889fc597a294ffdaad6e7dba49aa8af8830745d30a45061adbb67385a21ec2248ca8dc32640b73700f8278671280edb021
|
7
|
+
data.tar.gz: 60f3e3a064bcdcc8255ba3f0a52d4798ddbbf2a67b9588048b43e1b648ed1ebfe4d0cd747947df4980ea9616003e006937b9c6d99647e27db1ae3970667722e7
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,61 @@
|
|
1
|
+
## 1.1.0 - 2017-09-27
|
2
|
+
|
3
|
+
### Features
|
4
|
+
|
5
|
+
Check hooks, commands run by the Sensu client in response to the result of
|
6
|
+
the check command execution. The Sensu client will execute the appropriate
|
7
|
+
configured hook command, depending on the check execution status (e.g. 1).
|
8
|
+
Valid hook names include (in order of precedence): "1"-"255", "ok",
|
9
|
+
"warning", "critical", "unknown", and "non-zero". The check hook command
|
10
|
+
output, status, executed timestamp, and duration are captured and
|
11
|
+
published in the check result. Check hook commands can optionally receive
|
12
|
+
JSON serialized Sensu client and check definition data via STDIN.
|
13
|
+
|
14
|
+
Check STDIN. A boolean check definition attribute, `"stdin"`, when set to
|
15
|
+
`true` instructs the Sensu client to write JSON serialized Sensu client
|
16
|
+
and check definition data to the check command process STDIN. This
|
17
|
+
attribute cannot be used with existing Sensu check plugins, nor Nagios
|
18
|
+
plugins etc, as the Sensu client will wait indefinitely for the check
|
19
|
+
process to read and close STDIN.
|
20
|
+
|
21
|
+
Splayed proxy check request publishing. Users can now splay proxy check
|
22
|
+
requests (optional), evenly, over a window of time, determined by the
|
23
|
+
check interval and a configurable splay coverage percentage. For
|
24
|
+
example, if a check has an interval of 60s and a configured splay
|
25
|
+
coverage of 90%, its proxy check requests would be splayed evenly over a
|
26
|
+
time window of 60s * 90%, 54s, leaving 6s for the last proxy check
|
27
|
+
execution before the the next round of proxy check requests for the same
|
28
|
+
check. Proxy check request splayed publishing can be configured with two
|
29
|
+
new check definition attributes, within the `proxy_requests` scope,
|
30
|
+
`splay` (boolean) to enable it and `splay_coverage` (integer percentage,
|
31
|
+
defaults to `90`).
|
32
|
+
|
33
|
+
Configurable check output truncation (for storage in Redis). Check output
|
34
|
+
truncation can be manually enabled/disabled with the check definition
|
35
|
+
attribute "truncate_output", e.g.`"truncate_output": false`. The output
|
36
|
+
truncation length can be configured with the check definition attribute
|
37
|
+
"truncate_output_length", e.g. `"truncate_output_length": 1024`. Check
|
38
|
+
output truncation is still enabled by default for metric checks, with
|
39
|
+
`"type": "metric"`.
|
40
|
+
|
41
|
+
Sensu client HTTP socket basic authentication can how be applied to all
|
42
|
+
endpoints (not just `/settings`), via the client definition http_socket
|
43
|
+
attribute "protect_all_endpoints", e.g. `"protect_all_endpoints": true`.
|
44
|
+
|
45
|
+
### Other
|
46
|
+
|
47
|
+
Improved check TTL monitoring performance.
|
48
|
+
|
49
|
+
The Sensu extension run log event log level is now set to debug (instead
|
50
|
+
of info) when the run output is empty and the status is 0.
|
51
|
+
|
52
|
+
### Fixes
|
53
|
+
|
54
|
+
Added initial timestamp to proxy client definitions. The Uchiwa and Sensu
|
55
|
+
dashboards will no longer display "Invalid Date".
|
56
|
+
|
57
|
+
Deleting check history when deleting an associated check result.
|
58
|
+
|
1
59
|
## 1.0.3 - 2017-08-25
|
2
60
|
|
3
61
|
### Fixes
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
![sensu](https://raw.github.com/sensu/sensu/master/sensu-logo.png)
|
2
2
|
|
3
|
-
[![Build Status](https://img.shields.io/travis/sensu/sensu.
|
3
|
+
[![Build Status](https://img.shields.io/travis/sensu/sensu.svg)](https://travis-ci.org/sensu/sensu)
|
4
4
|
[![Gem Version](https://img.shields.io/gem/v/sensu.svg)](https://github.com/sensu/sensu/blob/master/CHANGELOG.md)
|
5
|
-
![MIT Licensed](https://img.shields.io/github/license/sensu/sensu.svg)
|
5
|
+
[![MIT Licensed](https://img.shields.io/github/license/sensu/sensu.svg)](https://raw.githubusercontent.com/sensu/sensu/master/MIT-LICENSE.txt)
|
6
6
|
|
7
7
|
# Sensu
|
8
8
|
|
@@ -27,7 +27,7 @@ on configuring and operating Sensu.
|
|
27
27
|
## Getting Help
|
28
28
|
|
29
29
|
If you have questions not covered by the documentation, the Sensu community
|
30
|
-
is here to help. Please check out our [chat][4] on
|
30
|
+
is here to help. Please check out our [chat][4] on Slack, or the
|
31
31
|
[sensu-users][5] discussion list.
|
32
32
|
|
33
33
|
Commercial support is also available. See the [support section of our website][6] for more detail.
|
@@ -45,7 +45,7 @@ Sensu Core is released under the [MIT license][8].
|
|
45
45
|
[1]: https://sensuapp.org/enterprise
|
46
46
|
[2]: https://sensuapp.org/docs/latest/platforms/
|
47
47
|
[3]: http://sensuapp.org/docs/latest/overview
|
48
|
-
[4]:
|
48
|
+
[4]: https://slack.sensu.io/
|
49
49
|
[5]: http://groups.google.com/group/sensu-users
|
50
50
|
[6]: https://sensuapp.org/support
|
51
51
|
[7]: https://github.com/sensu/sensu/blob/master/CONTRIBUTING.md
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require "sensu/utilities"
|
2
1
|
require "sensu/api/routes"
|
3
2
|
require "sensu/api/utilities/filter_response_content"
|
4
3
|
|
@@ -22,11 +21,9 @@ module Sensu
|
|
22
21
|
# @result [Hash]
|
23
22
|
def request_details
|
24
23
|
return @request_details if @request_details
|
25
|
-
@request_id = @http.fetch(:x_request_id, random_uuid)
|
26
24
|
@request_start_time = Time.now.to_f
|
27
25
|
_, remote_address = Socket.unpack_sockaddr_in(get_peername)
|
28
26
|
@request_details = {
|
29
|
-
:request_id => @request_id,
|
30
27
|
:remote_address => remote_address,
|
31
28
|
:user_agent => @http[:user_agent],
|
32
29
|
:method => @http_request_method,
|
@@ -47,9 +44,7 @@ module Sensu
|
|
47
44
|
end
|
48
45
|
|
49
46
|
# Log the HTTP response. This method calculates the
|
50
|
-
# request/response time.
|
51
|
-
# response body log event, as it is generally very verbose and
|
52
|
-
# unnecessary in most cases.
|
47
|
+
# request/response time.
|
53
48
|
def log_response
|
54
49
|
@logger.info("api response", {
|
55
50
|
:request => request_details,
|
@@ -57,12 +52,6 @@ module Sensu
|
|
57
52
|
:content_length => @response.content.to_s.bytesize,
|
58
53
|
:time => (Time.now.to_f - @request_start_time).round(3)
|
59
54
|
})
|
60
|
-
@logger.debug("api response body", {
|
61
|
-
:request => {
|
62
|
-
:request_id => @request_id
|
63
|
-
},
|
64
|
-
:content => @response.content
|
65
|
-
})
|
66
55
|
end
|
67
56
|
|
68
57
|
# Parse the HTTP request URI using a regular expression,
|
@@ -159,13 +148,6 @@ module Sensu
|
|
159
148
|
end
|
160
149
|
end
|
161
150
|
|
162
|
-
# Set the HTTP response headers, including the request ID and
|
163
|
-
# cors headers (via `set_cores_headers()`).
|
164
|
-
def set_headers
|
165
|
-
@response.headers["X-Request-ID"] = @request_id
|
166
|
-
set_cors_headers
|
167
|
-
end
|
168
|
-
|
169
151
|
# Paginate the provided items. This method uses two HTTP query
|
170
152
|
# parameters to determine how to paginate the items, `limit` and
|
171
153
|
# `offset`. The parameter `limit` specifies how many items are
|
@@ -410,7 +392,7 @@ module Sensu
|
|
410
392
|
log_request
|
411
393
|
parse_parameters
|
412
394
|
create_response
|
413
|
-
|
395
|
+
set_cors_headers
|
414
396
|
if authorized?
|
415
397
|
if connected?
|
416
398
|
route_request
|
@@ -87,6 +87,14 @@ module Sensu
|
|
87
87
|
false
|
88
88
|
end
|
89
89
|
|
90
|
+
def unauthorized_response
|
91
|
+
@logger.warn("http socket refusing to serve unauthorized request")
|
92
|
+
@response.headers["WWW-Authenticate"] = 'Basic realm="Sensu Client Restricted Area"'
|
93
|
+
send_response(401, "Unauthorized", {
|
94
|
+
:response => "You must be authenticated using your http_options user and password settings"
|
95
|
+
})
|
96
|
+
end
|
97
|
+
|
90
98
|
def send_response(status, status_string, content)
|
91
99
|
@logger.debug("http socket sending response", {
|
92
100
|
:status => status,
|
@@ -100,6 +108,9 @@ module Sensu
|
|
100
108
|
end
|
101
109
|
|
102
110
|
def process_request_info
|
111
|
+
if @settings[:client][:http_socket][:protect_all_endpoints]
|
112
|
+
return unauthorized_response unless authorized?
|
113
|
+
end
|
103
114
|
transport_info do |info|
|
104
115
|
send_response(200, "OK", {
|
105
116
|
:sensu => {
|
@@ -111,6 +122,9 @@ module Sensu
|
|
111
122
|
end
|
112
123
|
|
113
124
|
def process_request_results
|
125
|
+
if @settings[:client][:http_socket][:protect_all_endpoints]
|
126
|
+
return unauthorized_response unless authorized?
|
127
|
+
end
|
114
128
|
if @http[:content_type] and @http[:content_type] == "application/json" and @http_content
|
115
129
|
begin
|
116
130
|
check = Sensu::JSON::load(@http_content)
|
@@ -125,19 +139,12 @@ module Sensu
|
|
125
139
|
end
|
126
140
|
|
127
141
|
def process_request_settings
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
else
|
133
|
-
send_response(200, "OK", redact_sensitive(@settings.to_hash))
|
134
|
-
end
|
142
|
+
return unauthorized_response unless authorized?
|
143
|
+
@logger.info("http socket responding to request for configuration settings")
|
144
|
+
if @http_query_string and @http_query_string.downcase.include?("redacted=false")
|
145
|
+
send_response(200, "OK", @settings.to_hash)
|
135
146
|
else
|
136
|
-
|
137
|
-
@response.headers["WWW-Authenticate"] = 'Basic realm="Sensu Client Restricted Area"'
|
138
|
-
send_response(401, "Unauthorized", {
|
139
|
-
:response => "You must be authenticated using your http_options user and password settings"
|
140
|
-
})
|
147
|
+
send_response(200, "OK", redact_sensitive(@settings.to_hash))
|
141
148
|
end
|
142
149
|
end
|
143
150
|
|
@@ -181,7 +188,7 @@ module Sensu
|
|
181
188
|
:http_request_uri => @http_request_uri
|
182
189
|
})
|
183
190
|
send_response(405, "Method Not Allowed", {
|
184
|
-
:response => "Valid methods for this endpoint: #{
|
191
|
+
:response => "Valid methods for this endpoint: #{endpoint['methods'].keys}"
|
185
192
|
})
|
186
193
|
end
|
187
194
|
else
|
data/lib/sensu/client/process.rb
CHANGED
@@ -110,13 +110,66 @@ module Sensu
|
|
110
110
|
[check[:source], check[:name]].compact.join(":")
|
111
111
|
end
|
112
112
|
|
113
|
+
# Execute a check hook, capturing its output (STDOUT/ERR),
|
114
|
+
# exit status code, executed timestamp, and duration. This
|
115
|
+
# method determines which hook command to run by inspecting the
|
116
|
+
# check execution status. Check hook command tokens are
|
117
|
+
# substituted with the associated client attribute values, via
|
118
|
+
# `substitute_tokens()`. If there are unmatched check attribute
|
119
|
+
# value tokens, the check hook will not be executed, instead the
|
120
|
+
# hook command output will be set to report the unmatched
|
121
|
+
# tokens. Hook commands may expect/read and utilize JSON
|
122
|
+
# serialized Sensu client and check data via STDIN, if the hook
|
123
|
+
# definition includes `"stdin": true` (default is `false`). A
|
124
|
+
# hook may have a configured execution timeout, e.g. `"timeout":
|
125
|
+
# 30`, if one is not specified, the timeout defaults to 60
|
126
|
+
# seconds.
|
127
|
+
#
|
128
|
+
# @param check [Hash]
|
129
|
+
# @yield [check] callback/block called after executing the check
|
130
|
+
# hook (if any).
|
131
|
+
def execute_check_hook(check)
|
132
|
+
@logger.debug("attempting to execute check hook", :check => check)
|
133
|
+
severity = SEVERITIES[check[:status]] || "unknown"
|
134
|
+
hook = check[:hooks][check[:status].to_s] || check[:hooks][severity.to_sym]
|
135
|
+
if hook.nil? && check[:status] != 0
|
136
|
+
hook = check[:hooks]["non-zero"]
|
137
|
+
end
|
138
|
+
if hook
|
139
|
+
command, unmatched_tokens = substitute_tokens(hook[:command].dup, @settings[:client])
|
140
|
+
started = Time.now.to_f
|
141
|
+
hook[:executed] = started.to_i
|
142
|
+
if unmatched_tokens.empty?
|
143
|
+
options = {:timeout => hook.fetch(:timeout, 60)}
|
144
|
+
if hook[:stdin]
|
145
|
+
options[:data] = Sensu::JSON.dump({
|
146
|
+
:client => @settings[:client],
|
147
|
+
:check => check
|
148
|
+
})
|
149
|
+
end
|
150
|
+
Spawn.process(command, options) do |output, status|
|
151
|
+
hook[:duration] = ("%.3f" % (Time.now.to_f - started)).to_f
|
152
|
+
hook[:output] = output
|
153
|
+
hook[:status] = status
|
154
|
+
yield(check)
|
155
|
+
end
|
156
|
+
else
|
157
|
+
hook[:output] = "Unmatched client token(s): " + unmatched_tokens.join(", ")
|
158
|
+
hook[:status] = 3
|
159
|
+
yield(check)
|
160
|
+
end
|
161
|
+
else
|
162
|
+
yield(check)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
113
166
|
# Execute a check command, capturing its output (STDOUT/ERR),
|
114
167
|
# exit status code, execution duration, timestamp, and publish
|
115
168
|
# the result. This method guards against multiple executions for
|
116
169
|
# the same check. Check attribute value tokens are substituted
|
117
170
|
# with the associated client attribute values, via
|
118
|
-
# `object_substitute_tokens()`. The original check command
|
119
|
-
# always published, to guard against publishing
|
171
|
+
# `object_substitute_tokens()`. The original check command and
|
172
|
+
# hooks are always published, to guard against publishing
|
120
173
|
# sensitive/redacted client attribute values. If there are
|
121
174
|
# unmatched check attribute value tokens, the check will not be
|
122
175
|
# executed, instead a check result will be published reporting
|
@@ -129,16 +182,31 @@ module Sensu
|
|
129
182
|
unless @checks_in_progress.include?(in_progress_key)
|
130
183
|
@checks_in_progress << in_progress_key
|
131
184
|
substituted, unmatched_tokens = object_substitute_tokens(check.dup, @settings[:client])
|
132
|
-
check = substituted.merge(:command => check[:command])
|
185
|
+
check = substituted.merge(:command => check[:command], :hooks => check[:hooks])
|
186
|
+
check.delete(:hooks) if check[:hooks].nil?
|
133
187
|
started = Time.now.to_f
|
134
188
|
check[:executed] = started.to_i
|
135
189
|
if unmatched_tokens.empty?
|
136
|
-
|
190
|
+
options = {:timeout => check[:timeout]}
|
191
|
+
if check[:stdin]
|
192
|
+
options[:data] = Sensu::JSON.dump({
|
193
|
+
:client => @settings[:client],
|
194
|
+
:check => check
|
195
|
+
})
|
196
|
+
end
|
197
|
+
Spawn.process(substituted[:command], options) do |output, status|
|
137
198
|
check[:duration] = ("%.3f" % (Time.now.to_f - started)).to_f
|
138
199
|
check[:output] = output
|
139
200
|
check[:status] = status
|
140
|
-
|
141
|
-
|
201
|
+
if check[:hooks] && !check[:hooks].empty?
|
202
|
+
execute_check_hook(check) do |check|
|
203
|
+
publish_check_result(check)
|
204
|
+
@checks_in_progress.delete(in_progress_key)
|
205
|
+
end
|
206
|
+
else
|
207
|
+
publish_check_result(check)
|
208
|
+
@checks_in_progress.delete(in_progress_key)
|
209
|
+
end
|
142
210
|
end
|
143
211
|
else
|
144
212
|
check[:output] = "Unmatched client token(s): " + unmatched_tokens.join(", ")
|
@@ -178,8 +246,15 @@ module Sensu
|
|
178
246
|
check[:duration] = ("%.3f" % (Time.now.to_f - started)).to_f
|
179
247
|
check[:output] = output
|
180
248
|
check[:status] = status
|
181
|
-
|
182
|
-
|
249
|
+
if check[:hooks] && !check[:hooks].empty?
|
250
|
+
execute_check_hook(check) do |check|
|
251
|
+
publish_check_result(check)
|
252
|
+
@checks_in_progress.delete(in_progress_key)
|
253
|
+
end
|
254
|
+
else
|
255
|
+
publish_check_result(check)
|
256
|
+
@checks_in_progress.delete(in_progress_key)
|
257
|
+
end
|
183
258
|
end
|
184
259
|
else
|
185
260
|
@logger.warn("previous check extension execution in progress", :check => check)
|
data/lib/sensu/constants.rb
CHANGED
data/lib/sensu/daemon.rb
CHANGED
data/lib/sensu/server/handle.rb
CHANGED
@@ -126,7 +126,8 @@ module Sensu
|
|
126
126
|
# @param event_id [String] event UUID
|
127
127
|
def handler_extension(handler, event_data, event_id)
|
128
128
|
handler.safe_run(event_data) do |output, status|
|
129
|
-
|
129
|
+
log_level = (output.empty? && status.zero?) ? :debug : :info
|
130
|
+
@logger.send(log_level, "handler extension output", {
|
130
131
|
:extension => handler.definition,
|
131
132
|
:event => { :id => event_id },
|
132
133
|
:output => output,
|
data/lib/sensu/server/process.rb
CHANGED
@@ -320,16 +320,17 @@ module Sensu
|
|
320
320
|
end
|
321
321
|
end
|
322
322
|
|
323
|
-
# Truncate check output.
|
324
|
-
# "
|
325
|
-
#
|
326
|
-
#
|
323
|
+
# Truncate check output. Metric checks (`"type": "metric"`), or
|
324
|
+
# checks with `"truncate_output": true`, have their output
|
325
|
+
# truncated to a single line and a maximum character length of
|
326
|
+
# 255 by default. The maximum character length can be change by
|
327
|
+
# the `"truncate_output_length"` check definition attribute.
|
327
328
|
#
|
328
329
|
# @param check [Hash]
|
329
330
|
# @return [Hash] check with truncated output.
|
330
331
|
def truncate_check_output(check)
|
331
|
-
|
332
|
-
|
332
|
+
if check[:truncate_output] ||
|
333
|
+
(check[:type] == METRIC_CHECK_TYPE && check[:truncate_output] != false)
|
333
334
|
begin
|
334
335
|
output_lines = check[:output].split("\n")
|
335
336
|
rescue ArgumentError
|
@@ -341,8 +342,9 @@ module Sensu
|
|
341
342
|
output_lines = utf8_output.split("\n")
|
342
343
|
end
|
343
344
|
output = output_lines.first || check[:output]
|
344
|
-
|
345
|
-
|
345
|
+
truncate_output_length = check.fetch(:truncate_output_length, 255)
|
346
|
+
if output_lines.length > 1 || output.length > truncate_output_length
|
347
|
+
output = output[0..truncate_output_length] + "\n..."
|
346
348
|
end
|
347
349
|
check.merge(:output => output)
|
348
350
|
else
|
@@ -603,7 +605,8 @@ module Sensu
|
|
603
605
|
:address => "unknown",
|
604
606
|
:subscriptions => ["client:#{name}"],
|
605
607
|
:keepalives => false,
|
606
|
-
:version => VERSION
|
608
|
+
:version => VERSION,
|
609
|
+
:timestamp => Time.now.to_i
|
607
610
|
}
|
608
611
|
end
|
609
612
|
|
@@ -792,45 +795,129 @@ module Sensu
|
|
792
795
|
end
|
793
796
|
end
|
794
797
|
|
798
|
+
# Determine and return clients from the registry that match a
|
799
|
+
# set of attributes.
|
800
|
+
#
|
801
|
+
# @param clients [Array] of client names.
|
802
|
+
# @param attributes [Hash]
|
803
|
+
# @yield [Array] callback/block called after determining the
|
804
|
+
# matching clients, returning them as a block parameter.
|
805
|
+
def determine_matching_clients(clients, attributes)
|
806
|
+
client_keys = clients.map { |client_name| "client:#{client_name}" }
|
807
|
+
@redis.mget(*client_keys) do |client_json_objects|
|
808
|
+
matching_clients = []
|
809
|
+
client_json_objects.each do |client_json|
|
810
|
+
unless client_json.nil?
|
811
|
+
client = Sensu::JSON.load(client_json)
|
812
|
+
if attributes_match?(client, attributes)
|
813
|
+
matching_clients << client
|
814
|
+
end
|
815
|
+
end
|
816
|
+
end
|
817
|
+
yield(matching_clients)
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
# Publish a proxy check request for a client. This method
|
822
|
+
# substitutes client tokens in the check definition prior to
|
823
|
+
# publish the check request. If there are unmatched client
|
824
|
+
# tokens, a warning is logged, and a check request is not
|
825
|
+
# published.
|
826
|
+
#
|
827
|
+
# @param client [Hash] definition.
|
828
|
+
# @param check [Hash] definition.
|
829
|
+
def publish_proxy_check_request(client, check)
|
830
|
+
@logger.debug("creating a proxy check request", {
|
831
|
+
:client => client,
|
832
|
+
:check => check
|
833
|
+
})
|
834
|
+
proxy_check, unmatched_tokens = object_substitute_tokens(check.dup, client)
|
835
|
+
if unmatched_tokens.empty?
|
836
|
+
proxy_check[:source] ||= client[:name]
|
837
|
+
publish_check_request(proxy_check)
|
838
|
+
else
|
839
|
+
@logger.warn("failed to publish a proxy check request", {
|
840
|
+
:reason => "unmatched client tokens",
|
841
|
+
:unmatched_tokens => unmatched_tokens,
|
842
|
+
:client => client,
|
843
|
+
:check => check
|
844
|
+
})
|
845
|
+
end
|
846
|
+
end
|
847
|
+
|
848
|
+
# Publish proxy check requests for one or more clients. This
|
849
|
+
# method can optionally splay proxy check requests, evenly, over
|
850
|
+
# a period of time, determined by the check interval and a
|
851
|
+
# configurable splay coverage percentage. For example, splay
|
852
|
+
# proxy check requests over 60s * 90%, 54s, leaving 6s for the
|
853
|
+
# last proxy check execution before the the next round of proxy
|
854
|
+
# check requests for the same check. The
|
855
|
+
# `publish_proxy_check_request() method is used to publish the
|
856
|
+
# proxy check requests.
|
857
|
+
#
|
858
|
+
# @param clients [Array] of client definitions.
|
859
|
+
# @param check [Hash] definition.
|
860
|
+
def publish_proxy_check_requests(clients, check)
|
861
|
+
client_count = clients.length
|
862
|
+
splay = 0
|
863
|
+
if check[:proxy_requests][:splay]
|
864
|
+
interval = check[:interval]
|
865
|
+
if check[:cron]
|
866
|
+
interval = determine_check_cron_time(check)
|
867
|
+
end
|
868
|
+
unless interval.nil?
|
869
|
+
splay_coverage = check[:proxy_requests].fetch(:splay_coverage, 90)
|
870
|
+
splay = interval * (splay_coverage / 100.0) / client_count
|
871
|
+
end
|
872
|
+
end
|
873
|
+
splay_timer = 0
|
874
|
+
clients.each do |client|
|
875
|
+
unless splay == 0
|
876
|
+
EM::Timer.new(splay_timer) do
|
877
|
+
publish_proxy_check_request(client, check)
|
878
|
+
end
|
879
|
+
splay_timer += splay
|
880
|
+
else
|
881
|
+
publish_proxy_check_request(client, check)
|
882
|
+
end
|
883
|
+
end
|
884
|
+
end
|
885
|
+
|
795
886
|
# Create and publish one or more proxy check requests. This
|
796
887
|
# method iterates through the Sensu client registry for clients
|
797
888
|
# that matched provided proxy request client attributes. A proxy
|
798
889
|
# check request is created for each client in the registry that
|
799
890
|
# matches the proxy request client attributes. Proxy check
|
800
891
|
# requests have their client tokens subsituted by the associated
|
801
|
-
# client attributes values. The
|
802
|
-
#
|
892
|
+
# client attributes values. The `determine_matching_clients()`
|
893
|
+
# method is used to fetch and inspect each slide of clients from
|
894
|
+
# the registry, returning those that match the configured proxy
|
895
|
+
# request client attributes. A relatively small clients slice
|
896
|
+
# size (20) is used to reduce the number of clients inspected
|
897
|
+
# within a single tick of the EM reactor. The
|
898
|
+
# `publish_proxy_check_requests()` method is used to iterate
|
899
|
+
# through the matching Sensu clients, creating their own unique
|
900
|
+
# proxy check request, substituting client tokens, and then
|
901
|
+
# publishing them to the targetted subscriptions.
|
803
902
|
#
|
804
903
|
# @param check [Hash] definition.
|
805
|
-
def
|
904
|
+
def create_proxy_check_requests(check)
|
806
905
|
client_attributes = check[:proxy_requests][:client_attributes]
|
807
906
|
unless client_attributes.empty?
|
808
907
|
@redis.smembers("clients") do |clients|
|
809
|
-
clients.
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
:check => check
|
817
|
-
})
|
818
|
-
proxy_check, unmatched_tokens = object_substitute_tokens(check.dup, client)
|
819
|
-
if unmatched_tokens.empty?
|
820
|
-
proxy_check[:source] ||= client[:name]
|
821
|
-
publish_check_request(proxy_check)
|
822
|
-
else
|
823
|
-
@logger.warn("failed to publish a proxy check request", {
|
824
|
-
:reason => "unmatched client tokens",
|
825
|
-
:unmatched_tokens => unmatched_tokens,
|
826
|
-
:client => client,
|
827
|
-
:check => check
|
828
|
-
})
|
829
|
-
end
|
830
|
-
end
|
908
|
+
client_count = clients.length
|
909
|
+
proxy_check_requests = Proc.new do |matching_clients, slice_start, slice_size|
|
910
|
+
unless slice_start > client_count - 1
|
911
|
+
clients_slice = clients.slice(slice_start..slice_size)
|
912
|
+
determine_matching_clients(clients_slice, client_attributes) do |additional_clients|
|
913
|
+
matching_clients += additional_clients
|
914
|
+
proxy_check_requests.call(matching_clients, slice_start + 20, slice_size + 20)
|
831
915
|
end
|
916
|
+
else
|
917
|
+
publish_proxy_check_requests(matching_clients, check)
|
832
918
|
end
|
833
919
|
end
|
920
|
+
proxy_check_requests.call([], 0, 19)
|
834
921
|
end
|
835
922
|
end
|
836
923
|
end
|
@@ -846,7 +933,7 @@ module Sensu
|
|
846
933
|
Proc.new do
|
847
934
|
unless check_subdued?(check)
|
848
935
|
if check[:proxy_requests]
|
849
|
-
|
936
|
+
create_proxy_check_requests(check)
|
850
937
|
else
|
851
938
|
publish_check_request(check)
|
852
939
|
end
|
@@ -1064,46 +1151,83 @@ module Sensu
|
|
1064
1151
|
end
|
1065
1152
|
end
|
1066
1153
|
|
1154
|
+
# Create check TTL results. This method will retrieve check
|
1155
|
+
# results from the registry and determine the time since their
|
1156
|
+
# last check execution (in seconds). If the time since last
|
1157
|
+
# execution is equal to or greater than the defined check TTL, a
|
1158
|
+
# warning check result is published with the appropriate check
|
1159
|
+
# output.
|
1160
|
+
#
|
1161
|
+
# @param ttl_keys [Array] of TTL keys.
|
1162
|
+
# @param interval [Integer] to use for the check TTL result
|
1163
|
+
# interval.
|
1164
|
+
# @yield [] callback/block called after the check TTL results
|
1165
|
+
# have been created.
|
1166
|
+
def create_check_ttl_results(ttl_keys, interval=30)
|
1167
|
+
result_keys = ttl_keys.map { |ttl_key| "result:#{ttl_key}" }
|
1168
|
+
@redis.mget(*result_keys) do |result_json_objects|
|
1169
|
+
result_json_objects.each_with_index do |result_json, index|
|
1170
|
+
unless result_json.nil?
|
1171
|
+
check = Sensu::JSON.load(result_json)
|
1172
|
+
next unless check[:ttl] && check[:executed] && !check[:force_resolve]
|
1173
|
+
time_since_last_execution = Time.now.to_i - check[:executed]
|
1174
|
+
if time_since_last_execution >= check[:ttl]
|
1175
|
+
client_name = ttl_keys[index].split(":").first
|
1176
|
+
keepalive_event_exists?(client_name) do |event_exists|
|
1177
|
+
unless event_exists
|
1178
|
+
check[:output] = "Last check execution was "
|
1179
|
+
check[:output] << "#{time_since_last_execution} seconds ago"
|
1180
|
+
check[:status] = check[:ttl_status] || 1
|
1181
|
+
check[:interval] = interval
|
1182
|
+
publish_check_result(client_name, check)
|
1183
|
+
end
|
1184
|
+
end
|
1185
|
+
end
|
1186
|
+
else
|
1187
|
+
@redis.srem("ttl", result_key)
|
1188
|
+
end
|
1189
|
+
end
|
1190
|
+
yield
|
1191
|
+
end
|
1192
|
+
end
|
1193
|
+
|
1067
1194
|
# Determine stale check results, those that have not executed in
|
1068
1195
|
# a specified amount of time (check TTL). This method iterates
|
1069
1196
|
# through stored check results that have a defined TTL value (in
|
1070
|
-
# seconds). The
|
1071
|
-
#
|
1072
|
-
# execution
|
1073
|
-
#
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
check[:interval] = interval
|
1091
|
-
publish_check_result(client_name, check)
|
1092
|
-
end
|
1093
|
-
end
|
1094
|
-
end
|
1095
|
-
else
|
1096
|
-
@redis.srem("ttl", result_key)
|
1197
|
+
# seconds). The `create_check_ttl_results()` method is used to
|
1198
|
+
# inspect each check result, calculating their time since last
|
1199
|
+
# check execution (in seconds). If the time since last execution
|
1200
|
+
# is equal to or greater than the check TTL, a warning check
|
1201
|
+
# result is published with the appropriate check output. A
|
1202
|
+
# relatively small check results slice size (20) is used to
|
1203
|
+
# reduce the number of check results inspected within a single
|
1204
|
+
# tick of the EM reactor.
|
1205
|
+
#
|
1206
|
+
# @param interval [Integer] to use for the check TTL result
|
1207
|
+
# interval.
|
1208
|
+
def determine_stale_check_results(interval=30)
|
1209
|
+
@logger.info("determining stale check results (ttl)")
|
1210
|
+
@redis.smembers("ttl") do |ttl_keys|
|
1211
|
+
ttl_key_count = ttl_keys.length
|
1212
|
+
ttl_check_results = Proc.new do |slice_start, slice_size|
|
1213
|
+
unless slice_start > ttl_key_count - 1
|
1214
|
+
ttl_keys_slice = ttl_keys.slice(slice_start..slice_size)
|
1215
|
+
create_check_ttl_results(ttl_keys_slice, interval) do
|
1216
|
+
ttl_check_results.call(slice_start + 20, slice_size + 20)
|
1097
1217
|
end
|
1098
1218
|
end
|
1099
1219
|
end
|
1220
|
+
ttl_check_results.call(0, 19)
|
1100
1221
|
end
|
1101
1222
|
end
|
1102
1223
|
|
1103
1224
|
# Set up the check result monitor, a periodic timer to run
|
1104
1225
|
# `determine_stale_check_results()` every 30 seconds. The timer
|
1105
1226
|
# is stored in the timers hash under `:tasks`.
|
1106
|
-
|
1227
|
+
#
|
1228
|
+
# @param interval [Integer] to use for the check TTL result
|
1229
|
+
# interval.
|
1230
|
+
def setup_check_result_monitor(interval=30)
|
1107
1231
|
@logger.debug("monitoring check results")
|
1108
1232
|
@timers[:tasks][:check_result_monitor] << EM::PeriodicTimer.new(interval) do
|
1109
1233
|
determine_stale_check_results(interval)
|
data/sensu.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.add_dependency "eventmachine", "1.2.5"
|
16
16
|
s.add_dependency "sensu-json", "2.1.0"
|
17
17
|
s.add_dependency "sensu-logger", "1.2.1"
|
18
|
-
s.add_dependency "sensu-settings", "10.
|
18
|
+
s.add_dependency "sensu-settings", "10.9.0"
|
19
19
|
s.add_dependency "sensu-extension", "1.5.1"
|
20
20
|
s.add_dependency "sensu-extensions", "1.9.0"
|
21
21
|
s.add_dependency "sensu-transport", "7.0.2"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sensu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Porter
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-
|
12
|
+
date: 2017-09-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
@@ -59,14 +59,14 @@ dependencies:
|
|
59
59
|
requirements:
|
60
60
|
- - '='
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: 10.
|
62
|
+
version: 10.9.0
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - '='
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: 10.
|
69
|
+
version: 10.9.0
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: sensu-extension
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|