portertech-sensu 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +961 -0
  3. data/MIT-LICENSE.txt +20 -0
  4. data/README.md +65 -0
  5. data/exe/sensu-api +10 -0
  6. data/exe/sensu-client +10 -0
  7. data/exe/sensu-install +195 -0
  8. data/exe/sensu-server +10 -0
  9. data/lib/sensu/api/http_handler.rb +434 -0
  10. data/lib/sensu/api/process.rb +79 -0
  11. data/lib/sensu/api/routes/aggregates.rb +196 -0
  12. data/lib/sensu/api/routes/checks.rb +44 -0
  13. data/lib/sensu/api/routes/clients.rb +171 -0
  14. data/lib/sensu/api/routes/events.rb +86 -0
  15. data/lib/sensu/api/routes/health.rb +45 -0
  16. data/lib/sensu/api/routes/info.rb +37 -0
  17. data/lib/sensu/api/routes/request.rb +44 -0
  18. data/lib/sensu/api/routes/resolve.rb +32 -0
  19. data/lib/sensu/api/routes/results.rb +153 -0
  20. data/lib/sensu/api/routes/settings.rb +23 -0
  21. data/lib/sensu/api/routes/silenced.rb +182 -0
  22. data/lib/sensu/api/routes/stashes.rb +107 -0
  23. data/lib/sensu/api/routes.rb +88 -0
  24. data/lib/sensu/api/utilities/filter_response_content.rb +44 -0
  25. data/lib/sensu/api/utilities/publish_check_request.rb +107 -0
  26. data/lib/sensu/api/utilities/publish_check_result.rb +39 -0
  27. data/lib/sensu/api/utilities/resolve_event.rb +29 -0
  28. data/lib/sensu/api/utilities/servers_info.rb +43 -0
  29. data/lib/sensu/api/utilities/transport_info.rb +43 -0
  30. data/lib/sensu/api/validators/check.rb +55 -0
  31. data/lib/sensu/api/validators/client.rb +35 -0
  32. data/lib/sensu/api/validators/invalid.rb +8 -0
  33. data/lib/sensu/cli.rb +69 -0
  34. data/lib/sensu/client/http_socket.rb +217 -0
  35. data/lib/sensu/client/process.rb +655 -0
  36. data/lib/sensu/client/socket.rb +207 -0
  37. data/lib/sensu/client/utils.rb +53 -0
  38. data/lib/sensu/client/validators/check.rb +53 -0
  39. data/lib/sensu/constants.rb +17 -0
  40. data/lib/sensu/daemon.rb +396 -0
  41. data/lib/sensu/sandbox.rb +19 -0
  42. data/lib/sensu/server/filter.rb +227 -0
  43. data/lib/sensu/server/handle.rb +201 -0
  44. data/lib/sensu/server/mutate.rb +92 -0
  45. data/lib/sensu/server/process.rb +1646 -0
  46. data/lib/sensu/server/socket.rb +54 -0
  47. data/lib/sensu/server/tessen.rb +170 -0
  48. data/lib/sensu/utilities.rb +398 -0
  49. data/lib/sensu.rb +3 -0
  50. data/sensu.gemspec +36 -0
  51. 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