portertech-sensu 1.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,207 @@
|
|
1
|
+
require "sensu/json"
|
2
|
+
require "sensu/client/utils"
|
3
|
+
|
4
|
+
module Sensu
|
5
|
+
module Client
|
6
|
+
# EventMachine connection handler for the Sensu client"s socket.
|
7
|
+
#
|
8
|
+
# The Sensu client listens on localhost, port 3030 (by default), for
|
9
|
+
# UDP and TCP traffic. This allows software running on the host to
|
10
|
+
# push check results (that may contain metrics) into Sensu, without
|
11
|
+
# needing to know anything about Sensu"s internal implementation.
|
12
|
+
#
|
13
|
+
# The socket only accepts 7-bit ASCII-encoded data.
|
14
|
+
#
|
15
|
+
# Although the Sensu client accepts UDP and TCP traffic, you must be
|
16
|
+
# aware of the UDP protocol limitations. Any data you send over UDP
|
17
|
+
# must fit in a single datagram and you will not receive a response
|
18
|
+
# (no confirmation).
|
19
|
+
#
|
20
|
+
# == UDP Protocol ==
|
21
|
+
#
|
22
|
+
# If the socket receives a message containing whitespace and the
|
23
|
+
# string +"ping"+, it will ignore it.
|
24
|
+
#
|
25
|
+
# The socket assumes all other messages will contain a single,
|
26
|
+
# complete, JSON hash. The hash must be a valid JSON check result.
|
27
|
+
# Deserialization failures will be logged at the ERROR level by the
|
28
|
+
# Sensu client, but the sender of the invalid data will not be
|
29
|
+
# notified.
|
30
|
+
#
|
31
|
+
# == TCP Protocol ==
|
32
|
+
#
|
33
|
+
# If the socket receives a message containing whitespace and the
|
34
|
+
# string +"ping"+, it will respond with the message +"pong"+.
|
35
|
+
#
|
36
|
+
# The socket assumes any other stream will be a single, complete,
|
37
|
+
# JSON hash. A deserialization failure will be logged at the WARN
|
38
|
+
# level by the Sensu client and respond with the message
|
39
|
+
# +"invalid"+. An +"ok"+ response indicates the Sensu client
|
40
|
+
# successfully received the JSON hash and will publish the check
|
41
|
+
# result.
|
42
|
+
#
|
43
|
+
# Streams can be of any length. The socket protocol does not require
|
44
|
+
# any headers, instead the socket tries to parse everything it has
|
45
|
+
# been sent each time a chunk of data arrives. Once the JSON parses
|
46
|
+
# successfully, the Sensu client publishes the result. After
|
47
|
+
# +WATCHDOG_DELAY+ (default is 500 msec) since the most recent chunk
|
48
|
+
# of data was received, the agent will give up on the sender, and
|
49
|
+
# instead respond +"invalid"+ and close the connection.
|
50
|
+
class Socket < EM::Connection
|
51
|
+
include CheckUtils
|
52
|
+
|
53
|
+
attr_accessor :logger, :settings, :transport, :protocol
|
54
|
+
|
55
|
+
# The number of seconds that may elapse between chunks of data
|
56
|
+
# from a sender before it is considered dead, and the connection
|
57
|
+
# is close.
|
58
|
+
WATCHDOG_DELAY = 0.5
|
59
|
+
|
60
|
+
#
|
61
|
+
# Sensu::Socket operating mode enum.
|
62
|
+
#
|
63
|
+
|
64
|
+
# ACCEPT mode. Append chunks of data to a buffer and test to see
|
65
|
+
# whether the buffer contents are valid JSON.
|
66
|
+
MODE_ACCEPT = :ACCEPT
|
67
|
+
|
68
|
+
# REJECT mode. No longer receiving data from sender. Discard
|
69
|
+
# chunks of data in this mode, the connection is being closed.
|
70
|
+
MODE_REJECT = :REJECT
|
71
|
+
|
72
|
+
# PING request string, identifying a connection ping request.
|
73
|
+
PING_REQUEST = "ping".freeze
|
74
|
+
|
75
|
+
# Initialize instance variables that will be used throughout the
|
76
|
+
# lifetime of the connection. This method is called when the
|
77
|
+
# network connection has been established, and immediately after
|
78
|
+
# responding to a sender.
|
79
|
+
def post_init
|
80
|
+
@protocol ||= :tcp
|
81
|
+
@data_buffer = ""
|
82
|
+
@parse_error = nil
|
83
|
+
@watchdog = nil
|
84
|
+
@mode = MODE_ACCEPT
|
85
|
+
end
|
86
|
+
|
87
|
+
# Send a response to the sender, close the
|
88
|
+
# connection, and call post_init().
|
89
|
+
#
|
90
|
+
# @param [String] data to send as a response.
|
91
|
+
def respond(data)
|
92
|
+
if @protocol == :tcp
|
93
|
+
send_data(data)
|
94
|
+
close_connection_after_writing
|
95
|
+
end
|
96
|
+
post_init
|
97
|
+
end
|
98
|
+
|
99
|
+
# Cancel the current connection watchdog.
|
100
|
+
def cancel_watchdog
|
101
|
+
if @watchdog
|
102
|
+
@watchdog.cancel
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Reset (or start) the connection watchdog.
|
107
|
+
def reset_watchdog
|
108
|
+
cancel_watchdog
|
109
|
+
@watchdog = EM::Timer.new(WATCHDOG_DELAY) do
|
110
|
+
@mode = MODE_REJECT
|
111
|
+
@logger.warn("discarding data buffer for sender and closing connection", {
|
112
|
+
:data => @data_buffer,
|
113
|
+
:parse_error => @parse_error
|
114
|
+
})
|
115
|
+
respond("invalid")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Parse one or more JSON check results. For UDP, immediately
|
120
|
+
# raise a parser error. For TCP, record parser errors, so the
|
121
|
+
# connection +watchdog+ can report them.
|
122
|
+
#
|
123
|
+
# @param [String] data to parse for a check result.
|
124
|
+
def parse_check_result(data)
|
125
|
+
begin
|
126
|
+
object = Sensu::JSON.load(data)
|
127
|
+
cancel_watchdog
|
128
|
+
if object.is_a?(Array)
|
129
|
+
object.each do |check|
|
130
|
+
process_check_result(check)
|
131
|
+
end
|
132
|
+
else
|
133
|
+
process_check_result(object)
|
134
|
+
end
|
135
|
+
respond("ok")
|
136
|
+
rescue Sensu::JSON::ParseError, ArgumentError => error
|
137
|
+
if @protocol == :tcp
|
138
|
+
@parse_error = error.to_s
|
139
|
+
else
|
140
|
+
raise error
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Process the data received. This method validates the data
|
146
|
+
# encoding, provides ping/pong functionality, and passes potential
|
147
|
+
# check results on for further processing.
|
148
|
+
#
|
149
|
+
# @param [String] data to be processed.
|
150
|
+
def process_data(data)
|
151
|
+
if valid_utf8?(data) && data.strip == PING_REQUEST
|
152
|
+
@logger.debug("socket received ping")
|
153
|
+
respond("pong")
|
154
|
+
else
|
155
|
+
@logger.debug("socket received data", :data => data)
|
156
|
+
unless valid_utf8?(data)
|
157
|
+
@logger.warn("data from socket is not a valid UTF-8 sequence, processing it anyways", :data => data)
|
158
|
+
end
|
159
|
+
begin
|
160
|
+
parse_check_result(data)
|
161
|
+
rescue => error
|
162
|
+
@logger.error("failed to process check result from socket", {
|
163
|
+
:data => data,
|
164
|
+
:error => error.to_s
|
165
|
+
})
|
166
|
+
respond("invalid")
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Tests if the argument (data) is a valid UTF-8 sequence.
|
172
|
+
#
|
173
|
+
# @param [String] data to be tested.
|
174
|
+
def valid_utf8?(data)
|
175
|
+
utf8_string_pattern = /\A([\x00-\x7f]|
|
176
|
+
[\xc2-\xdf][\x80-\xbf]|
|
177
|
+
\xe0[\xa0-\xbf][\x80-\xbf]|
|
178
|
+
[\xe1-\xef][\x80-\xbf]{2}|
|
179
|
+
\xf0[\x90-\xbf][\x80-\xbf]{2}|
|
180
|
+
[\xf1-\xf7][\x80-\xbf]{3})*\z/nx
|
181
|
+
data = data.force_encoding('BINARY') if data.respond_to?(:force_encoding)
|
182
|
+
return data =~ utf8_string_pattern
|
183
|
+
end
|
184
|
+
|
185
|
+
# This method is called whenever data is received. For UDP, it
|
186
|
+
# will only be called once, the original data length can be
|
187
|
+
# expected. For TCP, this method may be called several times, data
|
188
|
+
# received is buffered. TCP connections require a +watchdog+.
|
189
|
+
#
|
190
|
+
# @param [String] data received from the sender.
|
191
|
+
def receive_data(data)
|
192
|
+
unless @mode == MODE_REJECT
|
193
|
+
case @protocol
|
194
|
+
when :udp
|
195
|
+
process_data(data)
|
196
|
+
when :tcp
|
197
|
+
if EM::reactor_running?
|
198
|
+
reset_watchdog
|
199
|
+
end
|
200
|
+
@data_buffer << data
|
201
|
+
process_data(@data_buffer)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "sensu/json"
|
2
|
+
require "sensu/client/validators/check"
|
3
|
+
|
4
|
+
module Sensu
|
5
|
+
module Client
|
6
|
+
module CheckUtils
|
7
|
+
class DataError < StandardError; end
|
8
|
+
|
9
|
+
# Validate check result attributes.
|
10
|
+
#
|
11
|
+
# @param [Hash] check result to validate.
|
12
|
+
def validate_check_result(check)
|
13
|
+
validator = Validators::Check.new
|
14
|
+
unless validator.valid?(check)
|
15
|
+
raise DataError, validator.failures.first[:message]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Process a check result. Set check result attribute defaults,
|
20
|
+
# validate the attributes, publish the check result to the Sensu
|
21
|
+
# transport, and respond to the sender with the message +"ok"+.
|
22
|
+
#
|
23
|
+
# @param [Hash] check result to be validated and published.
|
24
|
+
# @raise [DataError] if +check+ is invalid.
|
25
|
+
def process_check_result(check)
|
26
|
+
check[:status] ||= 0
|
27
|
+
check[:executed] ||= Time.now.to_i
|
28
|
+
validate_check_result(check)
|
29
|
+
publish_check_result(check)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Publish a check result to the Sensu transport.
|
33
|
+
#
|
34
|
+
# @param [Hash] check result.
|
35
|
+
def publish_check_result(check)
|
36
|
+
payload = {
|
37
|
+
:client => @settings[:client][:name],
|
38
|
+
:check => check.merge(:issued => Time.now.to_i)
|
39
|
+
}
|
40
|
+
payload[:signature] = @settings[:client][:signature] if @settings[:client][:signature]
|
41
|
+
@logger.info("publishing check result", :payload => payload)
|
42
|
+
@transport.publish(:direct, "results", Sensu::JSON.dump(payload)) do |info|
|
43
|
+
if info[:error]
|
44
|
+
@logger.error("failed to publish check result", {
|
45
|
+
:payload => payload,
|
46
|
+
:error => info[:error].to_s
|
47
|
+
})
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "sensu/settings/rules"
|
2
|
+
require "sensu/settings/validators/check"
|
3
|
+
|
4
|
+
module Sensu
|
5
|
+
module Client
|
6
|
+
module Validators
|
7
|
+
class Check
|
8
|
+
# Include Sensu Settings rules and check validator.
|
9
|
+
include Sensu::Settings::Rules
|
10
|
+
include Sensu::Settings::Validators::Check
|
11
|
+
|
12
|
+
attr_reader :failures
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@failures = []
|
16
|
+
end
|
17
|
+
|
18
|
+
# Determine if a check definition is valid.
|
19
|
+
#
|
20
|
+
# @param client [Hash]
|
21
|
+
# @return [TrueClass, FalseClass]
|
22
|
+
def valid?(check)
|
23
|
+
must_be_a_string(check[:output]) ||
|
24
|
+
invalid(check, "check output must be a string")
|
25
|
+
must_be_an_integer(check[:status]) ||
|
26
|
+
invalid(check, "check status must be an integer")
|
27
|
+
must_be_an_integer(check[:executed]) ||
|
28
|
+
invalid(check, "check executed timestamp must be an integer")
|
29
|
+
validate_check_name(check)
|
30
|
+
validate_check_handling(check)
|
31
|
+
validate_check_aggregate(check)
|
32
|
+
validate_check_flap_detection(check)
|
33
|
+
validate_check_truncate_output(check)
|
34
|
+
validate_check_source(check) if check[:source]
|
35
|
+
validate_check_ttl(check) if check[:ttl]
|
36
|
+
@failures.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# This method is called when `validate_check()` encounters an
|
42
|
+
# invalid definition object. This method adds definition
|
43
|
+
# validation failures to `@failures`.
|
44
|
+
def invalid(object, message)
|
45
|
+
@failures << {
|
46
|
+
:object => object,
|
47
|
+
:message => message
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Sensu
|
2
|
+
unless defined?(Sensu::VERSION)
|
3
|
+
# Sensu release version.
|
4
|
+
VERSION = "1.10.0".freeze
|
5
|
+
|
6
|
+
# Sensu release information.
|
7
|
+
RELEASE_INFO = {
|
8
|
+
:version => VERSION
|
9
|
+
}
|
10
|
+
|
11
|
+
# Sensu check severities.
|
12
|
+
SEVERITIES = %w[ok warning critical unknown].freeze
|
13
|
+
|
14
|
+
# Process signals that trigger a Sensu process stop.
|
15
|
+
STOP_SIGNALS = %w[INT TERM].freeze
|
16
|
+
end
|
17
|
+
end
|