sensu 0.16.0-java → 0.17.0.beta.1-java

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.
data/lib/sensu/socket.rb DELETED
@@ -1,246 +0,0 @@
1
- require 'multi_json'
2
-
3
- module Sensu
4
- # EventMachine connection handler for the Sensu client's socket.
5
- #
6
- # The Sensu client listens on localhost, port 3030 (by default), for
7
- # UDP and TCP traffic. This allows software running on the host to
8
- # push check results (that may contain metrics) into Sensu, without
9
- # needing to know anything about Sensu's internal implementation.
10
- #
11
- # The socket only accepts 7-bit ASCII-encoded data.
12
- #
13
- # Although the Sensu client accepts UDP and TCP traffic, you must be
14
- # aware of the UDP protocol limitations. Any data you send over UDP
15
- # must fit in a single datagram and you will not receive a response
16
- # (no confirmation).
17
- #
18
- # == UDP Protocol ==
19
- #
20
- # If the socket receives a message containing whitespace and the
21
- # string +'ping'+, it will ignore it.
22
- #
23
- # The socket assumes all other messages will contain a single,
24
- # complete, JSON hash. The hash must be a valid JSON check result.
25
- # Deserialization failures will be logged at the ERROR level by the
26
- # Sensu client, but the sender of the invalid data will not be
27
- # notified.
28
- #
29
- # == TCP Protocol ==
30
- #
31
- # If the socket receives a message containing whitespace and the
32
- # string +'ping'+, it will respond with the message +'pong'+.
33
- #
34
- # The socket assumes any other stream will be a single, complete,
35
- # JSON hash. A deserialization failure will be logged at the WARN
36
- # level by the Sensu client and respond with the message
37
- # +'invalid'+. An +'ok'+ response indicates the Sensu client
38
- # successfully received the JSON hash and will publish the check
39
- # result.
40
- #
41
- # Streams can be of any length. The socket protocol does not require
42
- # any headers, instead the socket tries to parse everything it has
43
- # been sent each time a chunk of data arrives. Once the JSON parses
44
- # successfully, the Sensu client publishes the result. After
45
- # +WATCHDOG_DELAY+ (default is 500 msec) since the most recent chunk
46
- # of data was received, the agent will give up on the sender, and
47
- # instead respond +'invalid'+ and close the connection.
48
- class Socket < EM::Connection
49
- class DataError < StandardError; end
50
-
51
- attr_accessor :logger, :settings, :transport, :protocol
52
-
53
- # The number of seconds that may elapse between chunks of data
54
- # from a sender before it is considered dead, and the connection
55
- # is close.
56
- WATCHDOG_DELAY = 0.5
57
-
58
- #
59
- # Sensu::Socket operating mode enum.
60
- #
61
-
62
- # ACCEPT mode. Append chunks of data to a buffer and test to see
63
- # whether the buffer contents are valid JSON.
64
- MODE_ACCEPT = :ACCEPT
65
-
66
- # REJECT mode. No longer receiving data from sender. Discard
67
- # chunks of data in this mode, the connection is being closed.
68
- MODE_REJECT = :REJECT
69
-
70
- # Initialize instance variables that will be used throughout the
71
- # lifetime of the connection. This method is called when the
72
- # network connection has been established, and immediately after
73
- # responding to a sender.
74
- def post_init
75
- @protocol ||= :tcp
76
- @data_buffer = ''
77
- @parse_error = nil
78
- @watchdog = nil
79
- @mode = MODE_ACCEPT
80
- end
81
-
82
- # Send a response to the sender, close the
83
- # connection, and call post_init().
84
- #
85
- # @param [String] data to send as a response.
86
- def respond(data)
87
- if @protocol == :tcp
88
- send_data(data)
89
- close_connection_after_writing
90
- end
91
- post_init
92
- end
93
-
94
- # Cancel the current connection watchdog.
95
- def cancel_watchdog
96
- if @watchdog
97
- @watchdog.cancel
98
- end
99
- end
100
-
101
- # Reset (or start) the connection watchdog.
102
- def reset_watchdog
103
- cancel_watchdog
104
- @watchdog = EM::Timer.new(WATCHDOG_DELAY) do
105
- @mode = MODE_REJECT
106
- @logger.warn('discarding data buffer for sender and closing connection', {
107
- :data => @data_buffer,
108
- :parse_error => @parse_error
109
- })
110
- respond('invalid')
111
- end
112
- end
113
-
114
- # Validate check result attributes.
115
- #
116
- # @param [Hash] check result to validate.
117
- def validate_check_result(check)
118
- unless check[:name] =~ /^[\w\.-]+$/
119
- raise DataError, 'check name must be a string and cannot contain spaces or special characters'
120
- end
121
- unless check[:output].is_a?(String)
122
- raise DataError, 'check output must be a string'
123
- end
124
- unless check[:status].is_a?(Integer)
125
- raise DataError, 'check status must be an integer'
126
- end
127
- end
128
-
129
- # Publish a check result to the Sensu transport.
130
- #
131
- # @param [Hash] check result.
132
- def publish_check_result(check)
133
- payload = {
134
- :client => @settings[:client][:name],
135
- :check => check.merge(:issued => Time.now.to_i)
136
- }
137
- @logger.info('publishing check result', {
138
- :payload => payload
139
- })
140
- @transport.publish(:direct, 'results', MultiJson.dump(payload))
141
- end
142
-
143
- # Process a check result. Set check result attribute defaults,
144
- # validate the attributes, publish the check result to the Sensu
145
- # transport, and respond to the sender with the message +'ok'+.
146
- #
147
- # @param [Hash] check result to be validated and published.
148
- # @raise [DataError] if +check+ is invalid.
149
- def process_check_result(check)
150
- check[:status] ||= 0
151
- validate_check_result(check)
152
- publish_check_result(check)
153
- respond('ok')
154
- end
155
-
156
- # Parse a JSON check result. For UDP, immediately raise a parser
157
- # error. For TCP, record parser errors, so the connection
158
- # +watchdog+ can report them.
159
- #
160
- # @param [String] data to parse for a check result.
161
- def parse_check_result(data)
162
- begin
163
- check = MultiJson.load(data)
164
- cancel_watchdog
165
- process_check_result(check)
166
- rescue MultiJson::ParseError, ArgumentError => error
167
- if @protocol == :tcp
168
- @parse_error = error.to_s
169
- else
170
- raise error
171
- end
172
- end
173
- end
174
-
175
- # Process the data received. This method validates the data
176
- # encoding, provides ping/pong functionality, and passes potential
177
- # check results on for further processing.
178
- #
179
- # @param [String] data to be processed.
180
- def process_data(data)
181
- if data.bytes.find { |char| char > 0x80 }
182
- @logger.warn('socket received non-ascii characters')
183
- respond('invalid')
184
- elsif data.strip == 'ping'
185
- @logger.debug('socket received ping')
186
- respond('pong')
187
- else
188
- @logger.debug('socket received data', {
189
- :data => data
190
- })
191
- begin
192
- parse_check_result(data)
193
- rescue => error
194
- @logger.error('failed to process check result from socket', {
195
- :data => data,
196
- :error => error.to_s
197
- })
198
- respond('invalid')
199
- end
200
- end
201
- end
202
-
203
- # This method is called whenever data is received. For UDP, it
204
- # will only be called once, the original data length can be
205
- # expected. For TCP, this method may be called several times, data
206
- # received is buffered. TCP connections require a +watchdog+.
207
- #
208
- # @param [String] data received from the sender.
209
- def receive_data(data)
210
- unless @mode == MODE_REJECT
211
- case @protocol
212
- when :udp
213
- process_data(data)
214
- when :tcp
215
- if EM.reactor_running?
216
- reset_watchdog
217
- end
218
- @data_buffer << data
219
- process_data(@data_buffer)
220
- end
221
- end
222
- end
223
- end
224
-
225
- class SocketHandler < EM::Connection
226
- attr_accessor :on_success, :on_error
227
-
228
- def connection_completed
229
- @connected_at = Time.now.to_f
230
- @inactivity_timeout = comm_inactivity_timeout
231
- end
232
-
233
- def unbind
234
- if @connected_at
235
- elapsed_time = Time.now.to_f - @connected_at
236
- if elapsed_time >= @inactivity_timeout
237
- @on_error.call('socket inactivity timeout')
238
- else
239
- @on_success.call('wrote to socket')
240
- end
241
- else
242
- @on_error.call('failed to connect to socket')
243
- end
244
- end
245
- end
246
- end