portertech-sensu 1.10.0
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 +7 -0
- data/CHANGELOG.md +961 -0
- data/MIT-LICENSE.txt +20 -0
- data/README.md +65 -0
- data/exe/sensu-api +10 -0
- data/exe/sensu-client +10 -0
- data/exe/sensu-install +195 -0
- data/exe/sensu-server +10 -0
- data/lib/sensu/api/http_handler.rb +434 -0
- data/lib/sensu/api/process.rb +79 -0
- data/lib/sensu/api/routes/aggregates.rb +196 -0
- data/lib/sensu/api/routes/checks.rb +44 -0
- data/lib/sensu/api/routes/clients.rb +171 -0
- data/lib/sensu/api/routes/events.rb +86 -0
- data/lib/sensu/api/routes/health.rb +45 -0
- data/lib/sensu/api/routes/info.rb +37 -0
- data/lib/sensu/api/routes/request.rb +44 -0
- data/lib/sensu/api/routes/resolve.rb +32 -0
- data/lib/sensu/api/routes/results.rb +153 -0
- data/lib/sensu/api/routes/settings.rb +23 -0
- data/lib/sensu/api/routes/silenced.rb +182 -0
- data/lib/sensu/api/routes/stashes.rb +107 -0
- data/lib/sensu/api/routes.rb +88 -0
- data/lib/sensu/api/utilities/filter_response_content.rb +44 -0
- data/lib/sensu/api/utilities/publish_check_request.rb +107 -0
- data/lib/sensu/api/utilities/publish_check_result.rb +39 -0
- data/lib/sensu/api/utilities/resolve_event.rb +29 -0
- data/lib/sensu/api/utilities/servers_info.rb +43 -0
- data/lib/sensu/api/utilities/transport_info.rb +43 -0
- data/lib/sensu/api/validators/check.rb +55 -0
- data/lib/sensu/api/validators/client.rb +35 -0
- data/lib/sensu/api/validators/invalid.rb +8 -0
- data/lib/sensu/cli.rb +69 -0
- data/lib/sensu/client/http_socket.rb +217 -0
- data/lib/sensu/client/process.rb +655 -0
- data/lib/sensu/client/socket.rb +207 -0
- data/lib/sensu/client/utils.rb +53 -0
- data/lib/sensu/client/validators/check.rb +53 -0
- data/lib/sensu/constants.rb +17 -0
- data/lib/sensu/daemon.rb +396 -0
- data/lib/sensu/sandbox.rb +19 -0
- data/lib/sensu/server/filter.rb +227 -0
- data/lib/sensu/server/handle.rb +201 -0
- data/lib/sensu/server/mutate.rb +92 -0
- data/lib/sensu/server/process.rb +1646 -0
- data/lib/sensu/server/socket.rb +54 -0
- data/lib/sensu/server/tessen.rb +170 -0
- data/lib/sensu/utilities.rb +398 -0
- data/lib/sensu.rb +3 -0
- data/sensu.gemspec +36 -0
- metadata +322 -0
@@ -0,0 +1,655 @@
|
|
1
|
+
require "sensu/daemon"
|
2
|
+
require "sensu/client/socket"
|
3
|
+
require "sensu/client/http_socket"
|
4
|
+
|
5
|
+
module Sensu
|
6
|
+
module Client
|
7
|
+
class Process
|
8
|
+
include Daemon
|
9
|
+
|
10
|
+
attr_accessor :safe_mode
|
11
|
+
|
12
|
+
# Create an instance of the Sensu client process, start the
|
13
|
+
# client within the EventMachine event loop, and set up client
|
14
|
+
# process signal traps (for stopping).
|
15
|
+
#
|
16
|
+
# @param options [Hash]
|
17
|
+
def self.run(options={})
|
18
|
+
client = self.new(options)
|
19
|
+
EM::run do
|
20
|
+
client.start
|
21
|
+
client.setup_signal_traps
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Override Daemon initialize() to support Sensu client check
|
26
|
+
# execution safe mode, checks in progress, and open sockets.
|
27
|
+
#
|
28
|
+
# @param options [Hash]
|
29
|
+
def initialize(options={})
|
30
|
+
super
|
31
|
+
@safe_mode = @settings[:client][:safe_mode] || false
|
32
|
+
@checks_in_progress = []
|
33
|
+
@sockets = []
|
34
|
+
end
|
35
|
+
|
36
|
+
# Create a Sensu client keepalive payload, to be sent over the
|
37
|
+
# transport for processing. A client keepalive is composed of
|
38
|
+
# its settings definition, the Sensu version, and a timestamp.
|
39
|
+
# Sensitive information is redacted from the keepalive payload.
|
40
|
+
#
|
41
|
+
# @return [Hash] keepalive payload
|
42
|
+
def keepalive_payload
|
43
|
+
payload = @settings[:client].merge({
|
44
|
+
:version => VERSION,
|
45
|
+
:timestamp => Time.now.to_i
|
46
|
+
})
|
47
|
+
redact_sensitive(payload, @settings[:client][:redact])
|
48
|
+
end
|
49
|
+
|
50
|
+
# Publish a Sensu client keepalive to the transport for
|
51
|
+
# processing. JSON serialization is used for transport messages.
|
52
|
+
def publish_keepalive
|
53
|
+
payload = keepalive_payload
|
54
|
+
@logger.debug("publishing keepalive", :payload => payload)
|
55
|
+
@transport.publish(:direct, "keepalives", Sensu::JSON.dump(payload)) do |info|
|
56
|
+
if info[:error]
|
57
|
+
@logger.error("failed to publish keepalive", {
|
58
|
+
:payload => payload,
|
59
|
+
:error => info[:error].to_s
|
60
|
+
})
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Schedule Sensu client keepalives. Immediately publish a
|
66
|
+
# keepalive to register the client, then publish a keepalive
|
67
|
+
# every 20 seconds. Sensu client keepalives are used to
|
68
|
+
# determine client (& machine) health.
|
69
|
+
def setup_keepalives
|
70
|
+
@logger.debug("scheduling keepalives")
|
71
|
+
publish_keepalive
|
72
|
+
@timers[:run] << EM::PeriodicTimer.new(20) do
|
73
|
+
publish_keepalive
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Publish a check result to the transport for processing. A
|
78
|
+
# check result is composed of a client (name) and a check
|
79
|
+
# definition, containing check `:output` and `:status`. JSON
|
80
|
+
# serialization is used when publishing the check result payload
|
81
|
+
# to the transport pipe. The check result is signed with the
|
82
|
+
# client signature if configured, for source validation.
|
83
|
+
# Transport errors are logged.
|
84
|
+
#
|
85
|
+
# @param check [Hash]
|
86
|
+
def publish_check_result(check)
|
87
|
+
check.delete(:source) if check[:source] == ""
|
88
|
+
payload = {
|
89
|
+
:client => @settings[:client][:name],
|
90
|
+
:check => check
|
91
|
+
}
|
92
|
+
payload[:signature] = @settings[:client][:signature] if @settings[:client][:signature]
|
93
|
+
@logger.info("publishing check result", :payload => payload)
|
94
|
+
@transport.publish(:direct, "results", Sensu::JSON.dump(payload)) do |info|
|
95
|
+
if info[:error]
|
96
|
+
@logger.error("failed to publish check result", {
|
97
|
+
:payload => payload,
|
98
|
+
:error => info[:error].to_s
|
99
|
+
})
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Create an in progress key for a check, used to determine if an
|
105
|
+
# execution is still in progress. The key is composed of check
|
106
|
+
# `source` (if set) and `name`, joined by a colon.
|
107
|
+
#
|
108
|
+
# @param check [Hash]
|
109
|
+
# @return [String]
|
110
|
+
def check_in_progress_key(check)
|
111
|
+
[check[:source], check[:name]].compact.join(":")
|
112
|
+
end
|
113
|
+
|
114
|
+
# Execute a check hook, capturing its output (STDOUT/ERR),
|
115
|
+
# exit status code, executed timestamp, and duration. This
|
116
|
+
# method determines which hook command to run by inspecting the
|
117
|
+
# check execution status. Check hook command tokens are
|
118
|
+
# substituted with the associated client attribute values, via
|
119
|
+
# `substitute_tokens()`. If there are unmatched check attribute
|
120
|
+
# value tokens, the check hook will not be executed, instead the
|
121
|
+
# hook command output will be set to report the unmatched
|
122
|
+
# tokens. Hook commands may expect/read and utilize JSON
|
123
|
+
# serialized Sensu client and check data via STDIN, if the hook
|
124
|
+
# definition includes `"stdin": true` (default is `false`). A
|
125
|
+
# hook may have a configured execution timeout, e.g. `"timeout":
|
126
|
+
# 30`, if one is not specified, the timeout defaults to 60
|
127
|
+
# seconds.
|
128
|
+
#
|
129
|
+
# @param check [Hash]
|
130
|
+
# @yield [check] callback/block called after executing the check
|
131
|
+
# hook (if any).
|
132
|
+
def execute_check_hook(check)
|
133
|
+
@logger.debug("attempting to execute check hook", :check => check)
|
134
|
+
severity = SEVERITIES[check[:status]] || "unknown"
|
135
|
+
hook = check[:hooks][check[:status].to_s.to_sym] || check[:hooks][severity.to_sym]
|
136
|
+
if hook.nil? && check[:status] != 0
|
137
|
+
hook = check[:hooks]["non-zero".to_sym]
|
138
|
+
end
|
139
|
+
if hook
|
140
|
+
command, unmatched_tokens = substitute_tokens(hook[:command].dup, @settings[:client])
|
141
|
+
started = Time.now.to_f
|
142
|
+
hook[:executed] = started.to_i
|
143
|
+
if unmatched_tokens.empty?
|
144
|
+
options = {:timeout => hook.fetch(:timeout, 60)}
|
145
|
+
if hook[:stdin]
|
146
|
+
options[:data] = Sensu::JSON.dump({
|
147
|
+
:client => @settings[:client],
|
148
|
+
:check => check
|
149
|
+
})
|
150
|
+
end
|
151
|
+
Spawn.process(command, options) do |output, status|
|
152
|
+
hook[:duration] = ("%.3f" % (Time.now.to_f - started)).to_f
|
153
|
+
hook[:output] = output
|
154
|
+
hook[:status] = status
|
155
|
+
yield(check)
|
156
|
+
end
|
157
|
+
else
|
158
|
+
hook[:output] = "Unmatched client token(s): " + unmatched_tokens.join(", ")
|
159
|
+
hook[:status] = 3
|
160
|
+
yield(check)
|
161
|
+
end
|
162
|
+
else
|
163
|
+
yield(check)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Execute a check command, capturing its output (STDOUT/ERR),
|
168
|
+
# exit status code, execution duration, timestamp, and publish
|
169
|
+
# the result. This method guards against multiple executions for
|
170
|
+
# the same check. Check attribute value tokens are substituted
|
171
|
+
# with the associated client attribute values, via
|
172
|
+
# `object_substitute_tokens()`. The original check command and
|
173
|
+
# hooks are always published, to guard against publishing
|
174
|
+
# sensitive/redacted client attribute values. If there are
|
175
|
+
# unmatched check attribute value tokens, the check will not be
|
176
|
+
# executed, instead a check result will be published reporting
|
177
|
+
# the unmatched tokens.
|
178
|
+
#
|
179
|
+
# @param check [Hash]
|
180
|
+
def execute_check_command(check)
|
181
|
+
@logger.debug("attempting to execute check command", :check => check)
|
182
|
+
in_progress_key = check_in_progress_key(check)
|
183
|
+
unless @checks_in_progress.include?(in_progress_key)
|
184
|
+
@checks_in_progress << in_progress_key
|
185
|
+
substituted, unmatched_tokens = object_substitute_tokens(check.dup, @settings[:client])
|
186
|
+
check = substituted.merge(:command => check[:command], :hooks => check[:hooks])
|
187
|
+
check.delete(:hooks) if check[:hooks].nil?
|
188
|
+
started = Time.now.to_f
|
189
|
+
check[:executed] = started.to_i
|
190
|
+
if unmatched_tokens.empty?
|
191
|
+
options = {:timeout => check[:timeout]}
|
192
|
+
if check[:stdin]
|
193
|
+
options[:data] = Sensu::JSON.dump({
|
194
|
+
:client => @settings[:client],
|
195
|
+
:check => check
|
196
|
+
})
|
197
|
+
end
|
198
|
+
Spawn.process(substituted[:command], options) do |output, status|
|
199
|
+
check[:duration] = ("%.3f" % (Time.now.to_f - started)).to_f
|
200
|
+
check[:output] = output
|
201
|
+
check[:status] = status
|
202
|
+
if check[:hooks] && !check[:hooks].empty?
|
203
|
+
execute_check_hook(check) do |check|
|
204
|
+
publish_check_result(check)
|
205
|
+
@checks_in_progress.delete(in_progress_key)
|
206
|
+
end
|
207
|
+
else
|
208
|
+
publish_check_result(check)
|
209
|
+
@checks_in_progress.delete(in_progress_key)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
else
|
213
|
+
check[:output] = "Unmatched client token(s): " + unmatched_tokens.join(", ")
|
214
|
+
check[:status] = 3
|
215
|
+
check[:handle] = false
|
216
|
+
publish_check_result(check)
|
217
|
+
@checks_in_progress.delete(in_progress_key)
|
218
|
+
end
|
219
|
+
else
|
220
|
+
@logger.warn("previous check command execution in progress", :check => check)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Run a check extension and publish the result. The Sensu client
|
225
|
+
# loads check extensions, checks that run within the Sensu Ruby
|
226
|
+
# VM and the EventMachine event loop, using the Sensu Extension
|
227
|
+
# API. If a check definition includes `:extension`, use it's
|
228
|
+
# value for the extension name, otherwise use the check name.
|
229
|
+
# The check definition is passed to the extension `safe_run()`
|
230
|
+
# method as a parameter, the extension may utilize it. This
|
231
|
+
# method guards against multiple executions for the same check
|
232
|
+
# extension.
|
233
|
+
#
|
234
|
+
# https://github.com/sensu/sensu-extension
|
235
|
+
#
|
236
|
+
# @param check [Hash]
|
237
|
+
def run_check_extension(check)
|
238
|
+
@logger.debug("attempting to run check extension", :check => check)
|
239
|
+
in_progress_key = check_in_progress_key(check)
|
240
|
+
unless @checks_in_progress.include?(in_progress_key)
|
241
|
+
@checks_in_progress << in_progress_key
|
242
|
+
started = Time.now.to_f
|
243
|
+
check[:executed] = started.to_i
|
244
|
+
extension_name = check[:extension] || check[:name]
|
245
|
+
extension = @extensions[:checks][extension_name]
|
246
|
+
extension.safe_run(check) do |output, status|
|
247
|
+
check[:duration] = ("%.3f" % (Time.now.to_f - started)).to_f
|
248
|
+
check[:output] = output
|
249
|
+
check[:status] = status
|
250
|
+
if check[:hooks] && !check[:hooks].empty?
|
251
|
+
execute_check_hook(check) do |check|
|
252
|
+
publish_check_result(check)
|
253
|
+
@checks_in_progress.delete(in_progress_key)
|
254
|
+
end
|
255
|
+
else
|
256
|
+
publish_check_result(check)
|
257
|
+
@checks_in_progress.delete(in_progress_key)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
else
|
261
|
+
@logger.warn("previous check extension execution in progress", :check => check)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Process a check request. If a check request has a check
|
266
|
+
# command, it will be executed. A standard check request will be
|
267
|
+
# merged with a local check definition, if present. Client safe
|
268
|
+
# mode is enforced in this method, requiring a local check
|
269
|
+
# definition in order to execute the check command. If a local
|
270
|
+
# check definition does not exist when operating with client
|
271
|
+
# safe mode, a check result will be published to report the
|
272
|
+
# missing check definition. A check request without a
|
273
|
+
# command indicates a check extension run. The check request may
|
274
|
+
# contain `:extension`, the name of the extension to run. If
|
275
|
+
# `:extension` is not present, the check name is used for the
|
276
|
+
# extension name. If a check extension does not exist for a
|
277
|
+
# name, a check result will be published to report the unknown
|
278
|
+
# check extension.
|
279
|
+
#
|
280
|
+
# @param check [Hash]
|
281
|
+
def process_check_request(check)
|
282
|
+
@logger.debug("processing check", :check => check)
|
283
|
+
if @settings.check_exists?(check[:name]) && !check.has_key?(:proxy_requests)
|
284
|
+
check.merge!(@settings[:checks][check[:name]])
|
285
|
+
end
|
286
|
+
if check.has_key?(:command)
|
287
|
+
if @safe_mode && !@settings.check_exists?(check[:name])
|
288
|
+
check[:output] = "Check is not locally defined (safe mode)"
|
289
|
+
check[:status] = 3
|
290
|
+
check[:handle] = false
|
291
|
+
check[:executed] = Time.now.to_i
|
292
|
+
publish_check_result(check)
|
293
|
+
else
|
294
|
+
execute_check_command(check)
|
295
|
+
end
|
296
|
+
else
|
297
|
+
extension_name = check[:extension] || check[:name]
|
298
|
+
if @extensions.check_exists?(extension_name)
|
299
|
+
run_check_extension(check)
|
300
|
+
else
|
301
|
+
@logger.warn("unknown check extension", :check => check)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
# Determine the Sensu transport subscribe options for a
|
307
|
+
# subscription. If a subscription begins with a transport pipe
|
308
|
+
# type, either "direct:" or "roundrobin:", the subscription uses
|
309
|
+
# a direct transport pipe, and the subscription name is used for
|
310
|
+
# both the pipe and the funnel names. If a subscription does not
|
311
|
+
# specify a transport pipe type, a fanout transport pipe is
|
312
|
+
# used, the subscription name is used for the pipe, and a unique
|
313
|
+
# funnel is created for the Sensu client. The unique funnel name
|
314
|
+
# for the Sensu client is created using a combination of the
|
315
|
+
# client name, the Sensu version, and the process start time
|
316
|
+
# (epoch).
|
317
|
+
#
|
318
|
+
# @param subscription [String]
|
319
|
+
# @return [Array] containing the transport subscribe options:
|
320
|
+
# the transport pipe type, pipe, and funnel.
|
321
|
+
def transport_subscribe_options(subscription)
|
322
|
+
_, raw_type = subscription.split(":", 2).reverse
|
323
|
+
case raw_type
|
324
|
+
when "direct", "roundrobin"
|
325
|
+
[:direct, subscription, subscription]
|
326
|
+
else
|
327
|
+
funnel = [@settings[:client][:name], VERSION, start_time].join("-")
|
328
|
+
[:fanout, subscription, funnel]
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# Set up Sensu client subscriptions. Subscriptions determine the
|
333
|
+
# kinds of check requests the client will receive. The Sensu
|
334
|
+
# client will receive JSON serialized check requests from its
|
335
|
+
# subscriptions, that get parsed and processed.
|
336
|
+
def setup_subscriptions
|
337
|
+
@logger.debug("subscribing to client subscriptions")
|
338
|
+
@settings[:client][:subscriptions].each do |subscription|
|
339
|
+
@logger.debug("subscribing to a subscription", :subscription => subscription)
|
340
|
+
options = transport_subscribe_options(subscription)
|
341
|
+
@transport.subscribe(*options) do |message_info, message|
|
342
|
+
begin
|
343
|
+
check = Sensu::JSON.load(message)
|
344
|
+
@logger.info("received check request", :check => check)
|
345
|
+
process_check_request(check)
|
346
|
+
rescue Sensu::JSON::ParseError => error
|
347
|
+
@logger.error("failed to parse the check request payload", {
|
348
|
+
:message => message,
|
349
|
+
:error => error.to_s
|
350
|
+
})
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
# Create a check execution proc, used to execute standalone
|
357
|
+
# checks. Checks are not executed if subdued. The check
|
358
|
+
# `:issued` timestamp is set here, to mimic check requests
|
359
|
+
# issued by a Sensu server. Check definitions are duplicated
|
360
|
+
# before processing them, in case they are mutated.
|
361
|
+
#
|
362
|
+
# @param check [Hash] definition.
|
363
|
+
def create_check_execution_proc(check)
|
364
|
+
Proc.new do
|
365
|
+
unless check_subdued?(check)
|
366
|
+
check[:issued] = Time.now.to_i
|
367
|
+
process_check_request(check.dup)
|
368
|
+
else
|
369
|
+
@logger.info("check execution was subdued", :check => check)
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
# Schedule a check execution, using the check cron. This method
|
375
|
+
# determines the time until the next cron time (in seconds) and
|
376
|
+
# creats an EventMachine timer for the execution. This method
|
377
|
+
# will be called after every check cron execution for subsequent
|
378
|
+
# executions. The timer is stored in the timers hash under
|
379
|
+
# `:run`, so it can be cancelled etc. The check cron execution
|
380
|
+
# timer object is removed from the timer hash after the
|
381
|
+
# execution, to stop the timer hash from growing infinitely.
|
382
|
+
#
|
383
|
+
# @param check [Hash] definition.
|
384
|
+
def schedule_check_cron_execution(check)
|
385
|
+
cron_time = determine_check_cron_time(check)
|
386
|
+
@timers[:run] << EM::Timer.new(cron_time) do |timer|
|
387
|
+
create_check_execution_proc(check).call
|
388
|
+
@timers[:run].delete(timer)
|
389
|
+
schedule_check_cron_execution(check)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
# Calculate a check execution splay, taking into account the
|
394
|
+
# current time and the execution interval to ensure it's
|
395
|
+
# consistent between process restarts.
|
396
|
+
#
|
397
|
+
# @param check [Hash] definition.
|
398
|
+
def calculate_check_execution_splay(check)
|
399
|
+
key = [@settings[:client][:name], check[:name]].join(":")
|
400
|
+
splay_hash = Digest::MD5.digest(key).unpack("Q<").first
|
401
|
+
current_time = (Time.now.to_f * 1000).to_i
|
402
|
+
(splay_hash - current_time) % (check[:interval] * 1000) / 1000.0
|
403
|
+
end
|
404
|
+
|
405
|
+
# Schedule check executions, using the check interval. This
|
406
|
+
# method using an intial calculated execution splay EventMachine
|
407
|
+
# timer and an EventMachine periodic timer for subsequent check
|
408
|
+
# executions. The timers are stored in the timers hash under
|
409
|
+
# `:run`, so they can be cancelled etc.
|
410
|
+
#
|
411
|
+
# @param check [Hash] definition.
|
412
|
+
def schedule_check_interval_executions(check)
|
413
|
+
execution_splay = testing? ? 0 : calculate_check_execution_splay(check)
|
414
|
+
interval = testing? ? 0.5 : check[:interval]
|
415
|
+
@timers[:run] << EM::Timer.new(execution_splay) do
|
416
|
+
execute_check = create_check_execution_proc(check)
|
417
|
+
execute_check.call
|
418
|
+
@timers[:run] << EM::PeriodicTimer.new(interval, &execute_check)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
# Schedule check executions. This method iterates through defined
|
423
|
+
# checks and uses the appropriate method of check execution
|
424
|
+
# scheduling, either with the cron syntax or a numeric interval.
|
425
|
+
#
|
426
|
+
# @param checks [Array] of definitions.
|
427
|
+
def schedule_checks(checks)
|
428
|
+
checks.each do |check|
|
429
|
+
if check[:cron]
|
430
|
+
schedule_check_cron_execution(check)
|
431
|
+
else
|
432
|
+
schedule_check_interval_executions(check)
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
# Setup standalone check executions, scheduling standard check
|
438
|
+
# definition and check extension executions. Check definitions
|
439
|
+
# and extensions with `:standalone` set to `true`, do not have
|
440
|
+
# `:publish` set to `false`, and have a integer `:interval` or a
|
441
|
+
# string `cron` will be scheduled by the Sensu client for
|
442
|
+
# execution.
|
443
|
+
def setup_standalone
|
444
|
+
@logger.debug("scheduling standalone checks")
|
445
|
+
standard_checks = @settings.checks.select do |check|
|
446
|
+
check[:standalone] && check[:publish] != false &&
|
447
|
+
(check[:interval].is_a?(Integer) || check[:cron].is_a?(String))
|
448
|
+
end
|
449
|
+
extension_checks = @extensions.checks.select do |check|
|
450
|
+
check[:standalone] && check[:publish] != false &&
|
451
|
+
(check[:interval].is_a?(Integer) || check[:cron].is_a?(String))
|
452
|
+
end
|
453
|
+
schedule_checks(standard_checks + extension_checks)
|
454
|
+
end
|
455
|
+
|
456
|
+
# Setup the Sensu client JSON socket, for external check result
|
457
|
+
# input. By default, the client socket is bound to localhost on
|
458
|
+
# TCP & UDP port 3030. The socket can be configured via the
|
459
|
+
# client definition, `:socket` with `:bind` and `:port`. Users can opt-out
|
460
|
+
# of using the TCP and UDP socket by setting `:enabled` to `false`.
|
461
|
+
# The current instance of the Sensu logger, settings, and transport
|
462
|
+
# are passed to the socket handler, `Sensu::Client::Socket`. The
|
463
|
+
# TCP socket server signature (Fixnum) and UDP connection object
|
464
|
+
# are stored in `@sockets`, so that they can be managed
|
465
|
+
# elsewhere, eg. `close_sockets()`.
|
466
|
+
def setup_json_socket
|
467
|
+
options = @settings[:client][:socket] || Hash.new
|
468
|
+
options[:bind] ||= "127.0.0.1"
|
469
|
+
options[:port] ||= 3030
|
470
|
+
unless options[:enabled] == false
|
471
|
+
@logger.debug("binding client tcp and udp sockets", :options => options)
|
472
|
+
@sockets << EM::start_server(options[:bind], options[:port], Socket) do |socket|
|
473
|
+
socket.logger = @logger
|
474
|
+
socket.settings = @settings
|
475
|
+
socket.transport = @transport
|
476
|
+
end
|
477
|
+
@sockets << EM::open_datagram_socket(options[:bind], options[:port], Socket) do |socket|
|
478
|
+
socket.logger = @logger
|
479
|
+
socket.settings = @settings
|
480
|
+
socket.transport = @transport
|
481
|
+
socket.protocol = :udp
|
482
|
+
end
|
483
|
+
else
|
484
|
+
@logger.info("client tcp/udp socket disabled per configuration")
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
# Setup the Sensu client HTTP socket, for external check result
|
489
|
+
# input and informational queries. By default, the client HTTP
|
490
|
+
# socket is bound to localhost on TCP port 3031. The socket can
|
491
|
+
# be configured via the client definition, `:http_socket` with
|
492
|
+
# `:bind` and `:port`. Users can opt-out of using the HTTP
|
493
|
+
# socket by setting `:enabled` to `false. The current instance
|
494
|
+
# of the Sensu logger, settings, and transport are passed to the
|
495
|
+
# HTTP socket handler, `Sensu::Client::HTTPSocket`. The HTTP
|
496
|
+
# socket server signature (Fixnum) is stored in `@sockets`, so
|
497
|
+
# that it can be managed elsewhere, eg. `close_sockets()`.
|
498
|
+
def setup_http_socket
|
499
|
+
options = @settings[:client][:http_socket] || Hash.new
|
500
|
+
options[:bind] ||= "127.0.0.1"
|
501
|
+
options[:port] ||= 3031
|
502
|
+
unless options[:enabled] == false
|
503
|
+
@logger.debug("binding client http socket", :options => options)
|
504
|
+
@sockets << EM::start_server(options[:bind], options[:port], HTTPSocket) do |socket|
|
505
|
+
socket.logger = @logger
|
506
|
+
socket.settings = @settings
|
507
|
+
socket.transport = @transport
|
508
|
+
end
|
509
|
+
else
|
510
|
+
@logger.info("client http socket disabled per configuration")
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
# Setup the Sensu client sockets, JSON TCP & UDP, and HTTP.
|
515
|
+
# Users can opt-out of using the HTTP socket via configuration.
|
516
|
+
def setup_sockets
|
517
|
+
setup_json_socket
|
518
|
+
setup_http_socket
|
519
|
+
end
|
520
|
+
|
521
|
+
# Call a callback (Ruby block) when there are no longer check
|
522
|
+
# executions in progress. This method is used when stopping the
|
523
|
+
# Sensu client. The `retry_until_true` helper method is used to
|
524
|
+
# check the condition every 0.5 seconds until `true` is
|
525
|
+
# returned.
|
526
|
+
#
|
527
|
+
# @yield [] callback/block called when there are no check
|
528
|
+
# executions in progress.
|
529
|
+
def complete_checks_in_progress
|
530
|
+
@logger.info("completing checks in progress", :checks_in_progress => @checks_in_progress)
|
531
|
+
retry_until_true do
|
532
|
+
if @checks_in_progress.empty?
|
533
|
+
yield
|
534
|
+
true
|
535
|
+
end
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
# Create a check result intended for deregistering a client.
|
540
|
+
# Client definitions may contain `:deregistration` configuration,
|
541
|
+
# containing custom attributes and handler information. By
|
542
|
+
# default, the deregistration check result sets the `:handler` to
|
543
|
+
# `deregistration`. If the client provides its own `:deregistration`
|
544
|
+
# configuration, it's deep merged with the defaults. The
|
545
|
+
# check `:name`, `:output`, `:issued`, and `:executed` values
|
546
|
+
# are always overridden to guard against an invalid definition.
|
547
|
+
def deregister
|
548
|
+
check = {:handler => "deregistration", :status => 1}
|
549
|
+
if @settings[:client].has_key?(:deregistration)
|
550
|
+
check = deep_merge(check, @settings[:client][:deregistration])
|
551
|
+
end
|
552
|
+
timestamp = Time.now.to_i
|
553
|
+
overrides = {
|
554
|
+
:name => "deregistration",
|
555
|
+
:output => "client initiated deregistration",
|
556
|
+
:issued => timestamp,
|
557
|
+
:executed => timestamp
|
558
|
+
}
|
559
|
+
publish_check_result(check.merge(overrides))
|
560
|
+
end
|
561
|
+
|
562
|
+
# Close the Sensu client TCP and UDP sockets. This method
|
563
|
+
# iterates through `@sockets`, which contains socket server
|
564
|
+
# signatures (Fixnum) and connection objects. A signature
|
565
|
+
# indicates a TCP socket server that needs to be stopped. A
|
566
|
+
# connection object indicates a socket connection that needs to
|
567
|
+
# be closed, eg. a UDP datagram socket.
|
568
|
+
def close_sockets
|
569
|
+
@logger.info("closing client sockets")
|
570
|
+
@sockets.each do |socket|
|
571
|
+
if socket.is_a?(Numeric)
|
572
|
+
EM.stop_server(socket)
|
573
|
+
else
|
574
|
+
socket.close_connection
|
575
|
+
end
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
# Bootstrap the Sensu client, setting up client keepalives,
|
580
|
+
# subscriptions, and standalone check executions. This method
|
581
|
+
# sets the process/daemon `@state` to `:running`.
|
582
|
+
def bootstrap
|
583
|
+
setup_keepalives
|
584
|
+
setup_subscriptions
|
585
|
+
setup_standalone
|
586
|
+
@state = :running
|
587
|
+
end
|
588
|
+
|
589
|
+
# Start the Sensu client process, setting up the client
|
590
|
+
# transport connection, the sockets, and calling the
|
591
|
+
# `bootstrap()` method.
|
592
|
+
def start
|
593
|
+
setup_transport do
|
594
|
+
setup_sockets
|
595
|
+
bootstrap
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
# Pause the Sensu client process, unless it is being paused or
|
600
|
+
# has already been paused. The process/daemon `@state` is first
|
601
|
+
# set to `:pausing`, to indicate that it's in progress. All run
|
602
|
+
# timers are cancelled, and the references are cleared. The
|
603
|
+
# Sensu client will unsubscribe from all transport
|
604
|
+
# subscriptions, then set the process/daemon `@state` to
|
605
|
+
# `:paused`.
|
606
|
+
def pause
|
607
|
+
unless @state == :pausing || @state == :paused
|
608
|
+
@state = :pausing
|
609
|
+
@timers[:run].each do |timer|
|
610
|
+
timer.cancel
|
611
|
+
end
|
612
|
+
@timers[:run].clear
|
613
|
+
@transport.unsubscribe if @transport
|
614
|
+
@state = :paused
|
615
|
+
end
|
616
|
+
end
|
617
|
+
|
618
|
+
# Resume the Sensu client process if it is currently or will
|
619
|
+
# soon be paused. The `retry_until_true` helper method is used
|
620
|
+
# to determine if the process is paused and if the transport is
|
621
|
+
# connected. If the conditions are met, `bootstrap()` will be
|
622
|
+
# called and true is returned to stop `retry_until_true`.
|
623
|
+
def resume
|
624
|
+
retry_until_true(1) do
|
625
|
+
if @state == :paused
|
626
|
+
if @transport.connected?
|
627
|
+
bootstrap
|
628
|
+
true
|
629
|
+
end
|
630
|
+
end
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
# Stop the Sensu client process, pausing it, completing check
|
635
|
+
# executions in progress, closing the transport connection, and
|
636
|
+
# exiting the process (exit 0). After pausing the process, the
|
637
|
+
# process/daemon `@state` is set to `:stopping`. Also sends
|
638
|
+
# deregistration check result if configured to do so.
|
639
|
+
def stop
|
640
|
+
@logger.warn("stopping")
|
641
|
+
last_state = @state
|
642
|
+
pause
|
643
|
+
if @settings[:client][:deregister] == true && last_state != :initializing
|
644
|
+
deregister
|
645
|
+
end
|
646
|
+
@state = :stopping
|
647
|
+
complete_checks_in_progress do
|
648
|
+
close_sockets
|
649
|
+
@transport.close if @transport
|
650
|
+
super
|
651
|
+
end
|
652
|
+
end
|
653
|
+
end
|
654
|
+
end
|
655
|
+
end
|