riemann-client 1.0.0 → 1.1.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 +4 -4
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/ci.yml +19 -5
- data/.github/workflows/codeql-analysis.yml +72 -0
- data/.rspec +2 -0
- data/.rubocop.yml +20 -0
- data/CHANGELOG.md +28 -2
- data/Gemfile +2 -0
- data/README.markdown +19 -10
- data/Rakefile +5 -2
- data/SECURITY.md +42 -0
- data/lib/riemann/attribute.rb +2 -0
- data/lib/riemann/auto_state.rb +9 -3
- data/lib/riemann/client/ssl_socket.rb +22 -21
- data/lib/riemann/client/tcp.rb +38 -38
- data/lib/riemann/client/tcp_socket.rb +66 -51
- data/lib/riemann/client/udp.rb +8 -4
- data/lib/riemann/client.rb +97 -78
- data/lib/riemann/event.rb +45 -51
- data/lib/riemann/message.rb +2 -0
- data/lib/riemann/metric_thread.rb +59 -54
- data/lib/riemann/query.rb +4 -2
- data/lib/riemann/state.rb +8 -8
- data/lib/riemann/version.rb +3 -1
- data/lib/riemann.rb +2 -3
- data/riemann-client.gemspec +10 -5
- data/spec/client_spec.rb +66 -0
- data/spec/shared_examples.rb +531 -0
- data/spec/spec_helper.rb +38 -0
- metadata +47 -10
- data/spec/client.rb +0 -384
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'socket'
|
2
4
|
require 'fcntl'
|
3
5
|
|
4
6
|
module Riemann
|
5
7
|
class Client
|
6
|
-
|
8
|
+
# Socket: A specialized socket that has been configure
|
7
9
|
class TcpSocket
|
8
10
|
class Error < Riemann::Client::Error; end
|
9
11
|
class Timeout < Error; end
|
@@ -68,7 +70,6 @@ module Riemann
|
|
68
70
|
# connection dead and notifying the application layer.
|
69
71
|
attr_reader :keepalive_count
|
70
72
|
|
71
|
-
|
72
73
|
# Internal: Create and connect to the given location.
|
73
74
|
#
|
74
75
|
# options, same as Constructor
|
@@ -77,11 +78,11 @@ module Riemann
|
|
77
78
|
def self.connect(options = {})
|
78
79
|
s = new(options)
|
79
80
|
s.connect
|
80
|
-
|
81
|
+
s
|
81
82
|
end
|
82
83
|
|
83
84
|
# Internal: Creates a new KJess::Socket
|
84
|
-
def initialize(
|
85
|
+
def initialize(options = {})
|
85
86
|
@host = options[:host]
|
86
87
|
@port = options[:port]
|
87
88
|
|
@@ -113,7 +114,7 @@ module Riemann
|
|
113
114
|
# Returns a new ::Socket instance for
|
114
115
|
|
115
116
|
def socket_factory(type)
|
116
|
-
|
117
|
+
sock = ::Socket.new(type, ::Socket::SOCK_STREAM, 0)
|
117
118
|
|
118
119
|
# close file descriptors if we exec
|
119
120
|
if Fcntl.constants.include?(:F_SETFD) && Fcntl.constants.include?(:FD_CLOEXEC)
|
@@ -122,14 +123,14 @@ module Riemann
|
|
122
123
|
# Disable Nagle's algorithm
|
123
124
|
sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
124
125
|
|
125
|
-
if using_keepalive?
|
126
|
-
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE
|
127
|
-
sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE
|
126
|
+
if using_keepalive?
|
127
|
+
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true)
|
128
|
+
sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE, keepalive_idle)
|
128
129
|
sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL, keepalive_interval)
|
129
|
-
sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT
|
130
|
+
sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT, keepalive_count)
|
130
131
|
end
|
131
132
|
|
132
|
-
|
133
|
+
sock
|
133
134
|
end
|
134
135
|
|
135
136
|
# Internal: Return the connected raw Socket.
|
@@ -139,7 +140,8 @@ module Riemann
|
|
139
140
|
# Returns a ::Socket
|
140
141
|
def socket
|
141
142
|
return @socket unless closed?
|
142
|
-
|
143
|
+
|
144
|
+
@socket ||= connect
|
143
145
|
end
|
144
146
|
|
145
147
|
# Internal: Closes the internal ::Socket
|
@@ -154,7 +156,8 @@ module Riemann
|
|
154
156
|
def closed?
|
155
157
|
return true if @socket.nil?
|
156
158
|
return true if @socket.closed?
|
157
|
-
|
159
|
+
|
160
|
+
false
|
158
161
|
end
|
159
162
|
|
160
163
|
# Internal:
|
@@ -169,16 +172,16 @@ module Riemann
|
|
169
172
|
deadline = Time.now.to_f + connect_timeout
|
170
173
|
|
171
174
|
# Lookup destination address, we only want TCP.
|
172
|
-
addrs = ::Socket.getaddrinfo(host, port, nil, ::Socket::SOCK_STREAM
|
175
|
+
addrs = ::Socket.getaddrinfo(host, port, nil, ::Socket::SOCK_STREAM)
|
173
176
|
errors = []
|
174
|
-
conn_error =
|
177
|
+
conn_error = -> { raise errors.first }
|
175
178
|
sock = nil
|
176
179
|
|
177
180
|
# Sort it so we get AF_INET, IPv4
|
178
|
-
addrs.sort.find(
|
179
|
-
sock = connect_or_error(
|
181
|
+
addrs.sort.find(conn_error) do |addr|
|
182
|
+
sock = connect_or_error(addr, deadline, errors)
|
180
183
|
end
|
181
|
-
|
184
|
+
sock
|
182
185
|
end
|
183
186
|
|
184
187
|
# Internal: Connect to the destination or raise an error.
|
@@ -195,13 +198,14 @@ module Riemann
|
|
195
198
|
# Should the connection fail, append the exception to the errors array and
|
196
199
|
# return false.
|
197
200
|
#
|
198
|
-
def connect_or_error(
|
201
|
+
def connect_or_error(addr, deadline, errors)
|
199
202
|
timeout = deadline - Time.now.to_f
|
200
203
|
raise Timeout, "Could not connect to #{host}:#{port}" if timeout <= 0
|
201
|
-
|
204
|
+
|
205
|
+
connect_nonblock(addr, timeout)
|
202
206
|
rescue Error => e
|
203
207
|
errors << e
|
204
|
-
|
208
|
+
false
|
205
209
|
end
|
206
210
|
|
207
211
|
# Internal: Connect to the give address within the timeout.
|
@@ -210,36 +214,47 @@ module Riemann
|
|
210
214
|
#
|
211
215
|
# Return the ::Socket when it is connected, or raise an Error if no
|
212
216
|
# connection was possible.
|
213
|
-
def connect_nonblock(
|
217
|
+
def connect_nonblock(addr, timeout)
|
214
218
|
sockaddr = ::Socket.pack_sockaddr_in(addr[1], addr[3])
|
215
|
-
sock =
|
216
|
-
sock.connect_nonblock(
|
217
|
-
|
219
|
+
sock = socket_factory(addr[4])
|
220
|
+
sock.connect_nonblock(sockaddr)
|
221
|
+
sock
|
218
222
|
rescue Errno::EINPROGRESS
|
219
223
|
if IO.select(nil, [sock], nil, timeout).nil?
|
220
|
-
|
224
|
+
begin
|
225
|
+
sock.close
|
226
|
+
rescue StandardError
|
227
|
+
nil
|
228
|
+
end
|
221
229
|
raise Timeout, "Could not connect to #{host}:#{port} within #{timeout} seconds"
|
222
230
|
end
|
223
|
-
|
224
|
-
rescue =>
|
225
|
-
|
226
|
-
|
231
|
+
connect_nonblock_finalize(sock, sockaddr)
|
232
|
+
rescue StandardError => e
|
233
|
+
begin
|
234
|
+
sock.close
|
235
|
+
rescue StandardError
|
236
|
+
nil
|
237
|
+
end
|
238
|
+
raise Error, "Could not connect to #{host}:#{port}: #{e.class}: #{e.message}", e.backtrace
|
227
239
|
end
|
228
240
|
|
229
|
-
|
230
241
|
# Internal: Make sure that a non-blocking connect has truely connected.
|
231
242
|
#
|
232
243
|
# Ensure that the given socket is actually connected to the given adddress.
|
233
244
|
#
|
234
245
|
# Returning the socket if it is and raising an Error if it isn't.
|
235
|
-
def connect_nonblock_finalize(
|
236
|
-
sock.connect_nonblock(
|
237
|
-
|
246
|
+
def connect_nonblock_finalize(sock, sockaddr)
|
247
|
+
sock.connect_nonblock(sockaddr)
|
248
|
+
sock
|
238
249
|
rescue Errno::EISCONN
|
239
|
-
|
240
|
-
rescue =>
|
241
|
-
|
242
|
-
|
250
|
+
sock
|
251
|
+
rescue StandardError => e
|
252
|
+
begin
|
253
|
+
sock.close
|
254
|
+
rescue StandardError
|
255
|
+
nil
|
256
|
+
end
|
257
|
+
raise Error, "Could not connect to #{host}:#{port}: #{e.class}: #{e.message}", e.backtrace
|
243
258
|
end
|
244
259
|
|
245
260
|
# Internal: say if we are using TCP Keep Alive or not
|
@@ -255,12 +270,12 @@ module Riemann
|
|
255
270
|
# Returns true or false
|
256
271
|
def using_keepalive?
|
257
272
|
using = false
|
258
|
-
if keepalive_active?
|
259
|
-
using = [
|
273
|
+
if keepalive_active?
|
274
|
+
using = %i[SOL_SOCKET SO_KEEPALIVE SOL_TCP TCP_KEEPIDLE TCP_KEEPINTVL TCP_KEEPCNT].all? do |c|
|
260
275
|
::Socket.const_defined? c
|
261
276
|
end
|
262
277
|
end
|
263
|
-
|
278
|
+
using
|
264
279
|
end
|
265
280
|
|
266
281
|
# Reads length bytes from the socket
|
@@ -274,18 +289,18 @@ module Riemann
|
|
274
289
|
outbuf.replace('')
|
275
290
|
buf = outbuf
|
276
291
|
else
|
277
|
-
buf =
|
292
|
+
buf = String.new
|
278
293
|
end
|
279
294
|
|
280
295
|
while buf.length < length
|
281
|
-
unless rb = readpartial(length - buf.length)
|
296
|
+
unless (rb = readpartial(length - buf.length))
|
282
297
|
break
|
283
298
|
end
|
284
299
|
|
285
300
|
buf << rb
|
286
301
|
end
|
287
302
|
|
288
|
-
|
303
|
+
buf
|
289
304
|
end
|
290
305
|
|
291
306
|
# Internal: Read up to a maxlen of data from the socket and store it in outbuf
|
@@ -295,13 +310,13 @@ module Riemann
|
|
295
310
|
#
|
296
311
|
# Returns the bytes read
|
297
312
|
def readpartial(maxlen, outbuf = nil)
|
298
|
-
|
313
|
+
socket.read_nonblock(maxlen, outbuf)
|
299
314
|
rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNRESET
|
300
|
-
|
301
|
-
retry
|
302
|
-
else
|
315
|
+
unless wait_readable(read_timeout)
|
303
316
|
raise Timeout, "Could not read from #{host}:#{port} in #{read_timeout} seconds"
|
304
317
|
end
|
318
|
+
|
319
|
+
retry
|
305
320
|
end
|
306
321
|
|
307
322
|
# Internal: Write the given data to the socket
|
@@ -313,16 +328,16 @@ module Riemann
|
|
313
328
|
#
|
314
329
|
# returns nothing
|
315
330
|
def write(buf)
|
316
|
-
until buf.nil?
|
331
|
+
until buf.nil? || buf.empty?
|
317
332
|
written = socket.write_nonblock(buf)
|
318
333
|
buf = buf[written, buf.length]
|
319
334
|
end
|
320
335
|
rescue Errno::EWOULDBLOCK, Errno::EINTR, Errno::EAGAIN, Errno::ECONNRESET
|
321
|
-
|
322
|
-
retry
|
323
|
-
else
|
336
|
+
unless wait_writable(write_timeout)
|
324
337
|
raise Timeout, "Could not write to #{host}:#{port} in #{write_timeout} seconds"
|
325
338
|
end
|
339
|
+
|
340
|
+
retry
|
326
341
|
end
|
327
342
|
|
328
343
|
def wait_writable(timeout = nil)
|
data/lib/riemann/client/udp.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Riemann
|
2
4
|
class Client
|
3
5
|
class UDP < Client
|
4
|
-
MAX_SIZE =
|
6
|
+
MAX_SIZE = 16_384
|
5
7
|
|
6
8
|
attr_accessor :host, :port, :max_size
|
7
9
|
|
8
|
-
def initialize(opts = {})
|
10
|
+
def initialize(opts = {}) # rubocop:disable Lint/MissingSuper
|
9
11
|
@host = opts[:host] || HOST
|
10
12
|
@port = opts[:port] || PORT
|
11
13
|
@max_size = opts[:max_size] || MAX_SIZE
|
@@ -13,6 +15,7 @@ module Riemann
|
|
13
15
|
|
14
16
|
def socket
|
15
17
|
return @socket if connected?
|
18
|
+
|
16
19
|
@socket = UDPSocket.new
|
17
20
|
end
|
18
21
|
|
@@ -26,17 +29,18 @@ module Riemann
|
|
26
29
|
end
|
27
30
|
|
28
31
|
# Read a message from a stream
|
29
|
-
def read_message(
|
32
|
+
def read_message(_socket)
|
30
33
|
raise Unsupported
|
31
34
|
end
|
32
35
|
|
33
|
-
def send_recv(
|
36
|
+
def send_recv(_message)
|
34
37
|
raise Unsupported
|
35
38
|
end
|
36
39
|
|
37
40
|
def send_maybe_recv(message)
|
38
41
|
encoded_string = message.encode.to_s
|
39
42
|
raise TooBig unless encoded_string.length < @max_size
|
43
|
+
|
40
44
|
socket.send(encoded_string, 0, @host, @port)
|
41
45
|
nil
|
42
46
|
end
|
data/lib/riemann/client.rb
CHANGED
@@ -1,111 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'riemann'
|
2
4
|
|
3
|
-
|
4
|
-
class
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
module Riemann
|
6
|
+
class Client
|
7
|
+
class Error < RuntimeError; end
|
8
|
+
class InvalidResponse < Error; end
|
9
|
+
class ServerError < Error; end
|
10
|
+
class Unsupported < Error; end
|
11
|
+
class TooBig < Unsupported; end
|
12
|
+
|
13
|
+
require 'socket'
|
14
|
+
require 'time'
|
9
15
|
|
10
|
-
|
11
|
-
|
12
|
-
|
16
|
+
HOST = '127.0.0.1'
|
17
|
+
PORT = 5555
|
18
|
+
TIMEOUT = 5
|
13
19
|
|
14
|
-
|
15
|
-
|
16
|
-
TIMEOUT = 5
|
20
|
+
require 'riemann/client/tcp'
|
21
|
+
require 'riemann/client/udp'
|
17
22
|
|
18
|
-
|
19
|
-
require 'riemann/client/udp'
|
23
|
+
attr_reader :tcp, :udp
|
20
24
|
|
21
|
-
|
25
|
+
def initialize(opts = {})
|
26
|
+
@options = opts.dup
|
27
|
+
@options[:host] ||= HOST
|
28
|
+
@options[:port] ||= PORT
|
29
|
+
@options[:timeout] ||= TIMEOUT
|
22
30
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
@options[:port] ||= PORT
|
27
|
-
@options[:timeout] ||= TIMEOUT
|
31
|
+
@udp = UDP.new(@options)
|
32
|
+
@tcp = TCP.new(@options)
|
33
|
+
return unless block_given?
|
28
34
|
|
29
|
-
@udp = UDP.new(@options)
|
30
|
-
@tcp = TCP.new(@options)
|
31
|
-
if block_given?
|
32
35
|
begin
|
33
36
|
yield self
|
34
37
|
ensure
|
35
38
|
close
|
36
39
|
end
|
37
40
|
end
|
38
|
-
end
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
-
|
42
|
+
def host
|
43
|
+
@options[:host]
|
44
|
+
end
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
|
46
|
+
def port
|
47
|
+
@options[:port]
|
48
|
+
end
|
47
49
|
|
48
|
-
|
49
|
-
|
50
|
-
|
50
|
+
def timeout
|
51
|
+
@options[:timeout]
|
52
|
+
end
|
51
53
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
else
|
61
|
-
unless event_opts.include? :host
|
62
|
-
event_opts[:host] = Socket.gethostname
|
54
|
+
# Send a state
|
55
|
+
def <<(event)
|
56
|
+
# Create state
|
57
|
+
case event
|
58
|
+
when Riemann::State, Riemann::Event, Hash
|
59
|
+
# Noop
|
60
|
+
else
|
61
|
+
raise(ArgumentError, "Unsupported event class: #{event.class.name}")
|
63
62
|
end
|
64
|
-
|
63
|
+
|
64
|
+
bulk_send([event])
|
65
65
|
end
|
66
66
|
|
67
|
-
|
67
|
+
def bulk_send(events)
|
68
|
+
raise ArgumentError unless events.is_a?(Array)
|
68
69
|
|
69
|
-
|
70
|
-
send_maybe_recv message
|
71
|
-
end
|
70
|
+
message = Riemann::Message.new(events: normalize_events(events))
|
72
71
|
|
73
|
-
|
74
|
-
|
75
|
-
response = query(query)
|
76
|
-
(response.events || []) |
|
77
|
-
(response.states || [])
|
78
|
-
end
|
72
|
+
send_maybe_recv(message)
|
73
|
+
end
|
79
74
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
75
|
+
def normalize_events(events)
|
76
|
+
events.map do |event|
|
77
|
+
case event
|
78
|
+
when Riemann::State, Riemann::Event
|
79
|
+
event
|
80
|
+
when Hash
|
81
|
+
e = if event.include?(:host)
|
82
|
+
event
|
83
|
+
else
|
84
|
+
event.dup.merge(host: Socket.gethostname)
|
85
|
+
end
|
86
|
+
Riemann::Event.new(e)
|
87
|
+
else
|
88
|
+
raise(ArgumentError, "Unsupported event class: #{event.class.name}")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
84
92
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
93
|
+
# Returns an array of states matching query.
|
94
|
+
def [](query)
|
95
|
+
response = query(query)
|
96
|
+
(response.events || []) |
|
97
|
+
(response.states || [])
|
98
|
+
end
|
90
99
|
|
91
|
-
|
92
|
-
|
93
|
-
|
100
|
+
def connect
|
101
|
+
# NOTE: connections are made automatically on send
|
102
|
+
warn 'Riemann client#connect is deprecated'
|
103
|
+
end
|
94
104
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
105
|
+
# Close both UDP and TCP sockets.
|
106
|
+
def close
|
107
|
+
@udp.close
|
108
|
+
@tcp.close
|
109
|
+
end
|
99
110
|
|
100
|
-
|
101
|
-
|
102
|
-
|
111
|
+
def connected?
|
112
|
+
tcp.connected? and udp.connected?
|
113
|
+
end
|
114
|
+
|
115
|
+
# Ask for states
|
116
|
+
def query(string = 'true')
|
117
|
+
send_recv Riemann::Message.new(query: Riemann::Query.new(string: string))
|
118
|
+
end
|
119
|
+
|
120
|
+
def send_recv(message)
|
121
|
+
@tcp.send_recv(message)
|
122
|
+
end
|
103
123
|
|
104
|
-
|
105
|
-
|
106
|
-
@udp.send_maybe_recv *a
|
124
|
+
def send_maybe_recv(message)
|
125
|
+
@udp.send_maybe_recv(message)
|
107
126
|
rescue TooBig
|
108
|
-
@tcp.send_maybe_recv
|
127
|
+
@tcp.send_maybe_recv(message)
|
109
128
|
end
|
110
129
|
end
|
111
130
|
end
|