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.
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