riemann-client 0.2.2 → 0.2.3

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 971fad804164c0db71b3e689d9b866ad89a20e41
4
+ data.tar.gz: 1e7f35daae4ab58fa8018225959da905bfd5f77b
5
+ SHA512:
6
+ metadata.gz: 904df6b31de7feda767274d2a776040287d2b3849280257abd5b167171adf990812b5deabfdde8a48a220b329564476c93a844bb195a11655736578784c84a3c
7
+ data.tar.gz: 3ccaa03c50e24502eee6e9e440279a1d5989b776f70ad04536654e3c887c3f14fb841502593fb84d3dbd9256749a7babb6870d4a7f4f9eb1c259f23f39b5e14f
@@ -18,14 +18,28 @@ class Riemann::Client
18
18
  require 'riemann/client/tcp'
19
19
  require 'riemann/client/udp'
20
20
 
21
- attr_accessor :host, :port, :tcp, :udp, :timeout
21
+ attr_reader :tcp, :udp
22
22
 
23
23
  def initialize(opts = {})
24
- @host = opts[:host] || HOST
25
- @port = opts[:port] || PORT
26
- @timeout = opts[:timeout] || TIMEOUT
27
- @udp = UDP.new opts
28
- @tcp = TCP.new opts
24
+ @options = opts.dup
25
+ @options[:host] ||= HOST
26
+ @options[:port] ||= PORT
27
+ @options[:timeout] ||= TIMEOUT
28
+
29
+ @udp = UDP.new(@options)
30
+ @tcp = TCP.new(@options)
31
+ end
32
+
33
+ def host
34
+ @options[:host]
35
+ end
36
+
37
+ def port
38
+ @options[:port]
39
+ end
40
+
41
+ def timeout
42
+ @options[:timeout]
29
43
  end
30
44
 
31
45
  # Send a state
@@ -1,24 +1,50 @@
1
+ require 'monitor'
2
+ require 'riemann/client/tcp_socket'
3
+
1
4
  module Riemann
2
5
  class Client
3
6
  class TCP < Client
4
7
  attr_accessor :host, :port, :socket
5
8
 
6
- def initialize(opts = {})
7
- @host = opts[:host] || HOST
8
- @port = opts[:port] || PORT
9
- @timeout = opts[:timeout] || TIMEOUT
10
- @locket = Mutex.new
9
+ # Public: Set a socket factory -- an object responding
10
+ # to #call(options) that returns a Socket object
11
+ def self.socket_factory=(factory)
12
+ @socket_factory = factory
13
+ end
14
+
15
+ # Public: Return a socket factory
16
+ def self.socket_factory
17
+ @socket_factory || proc { |options| TcpSocket.connect(options) }
18
+ end
19
+
20
+ def initialize(options = {})
21
+ @options = options
22
+ @locket = Monitor.new
11
23
  end
12
24
 
13
- def connect
14
- Timeout::timeout(@timeout) do
15
- @socket = TCPSocket.new(@host, @port)
25
+ def socket
26
+ @locket.synchronize do
27
+ if @pid && @pid != Process.pid
28
+ close
29
+ end
30
+
31
+ if @socket and not @socket.closed?
32
+ return @socket
33
+ end
34
+
35
+ @socket = self.class.socket_factory.call(@options)
36
+ @pid = Process.pid
37
+
38
+ return @socket
16
39
  end
17
40
  end
18
41
 
19
42
  def close
20
43
  @locket.synchronize do
21
- @socket.close
44
+ if @socket && !@socket.closed?
45
+ @socket.close
46
+ end
47
+ @socket = nil
22
48
  end
23
49
  end
24
50
 
@@ -51,8 +77,8 @@ module Riemann
51
77
 
52
78
  def send_recv(message)
53
79
  with_connection do |s|
54
- s << message.encode_with_length
55
- read_message s
80
+ s.write(message.encode_with_length)
81
+ read_message(s)
56
82
  end
57
83
  end
58
84
 
@@ -65,25 +91,14 @@ module Riemann
65
91
  @locket.synchronize do
66
92
  begin
67
93
  tries += 1
68
- yield(@socket || connect)
69
- rescue IOError => e
70
- raise if tries > 3
71
- connect and retry
72
- rescue Errno::EPIPE => e
73
- raise if tries > 3
74
- connect and retry
75
- rescue Errno::ECONNREFUSED => e
94
+ yield(socket)
95
+ rescue IOError, Errno::EPIPE, Errno::ECONNREFUSED, InvalidResponse, Timeout::Error, Riemann::Client::TcpSocket::Error
96
+ close
76
97
  raise if tries > 3
77
- connect and retry
78
- rescue Errno::ECONNRESET => e
79
- raise if tries > 3
80
- connect and retry
81
- rescue InvalidResponse => e
82
- raise if tries > 3
83
- connect and retry
84
- rescue Timeout::Error => e
85
- raise if tries > 3
86
- connect and retry
98
+ retry
99
+ rescue Exception
100
+ close
101
+ raise
87
102
  end
88
103
  end
89
104
  end
@@ -0,0 +1,334 @@
1
+ require 'socket'
2
+ require 'fcntl'
3
+
4
+ module Riemann
5
+ class Client
6
+ # Socket: A specialized socket that has been configure
7
+ class TcpSocket
8
+ class Error < Riemann::Client::Error; end
9
+ class Timeout < Error; end
10
+
11
+ # Internal:
12
+ # The timeout for reading in seconds. Defaults to 2
13
+ attr_accessor :read_timeout
14
+
15
+ # Internal:
16
+ # The timeout for connecting in seconds. Defaults to 2
17
+ attr_reader :connect_timeout
18
+
19
+ # Internal:
20
+ # The timeout for writing in seconds. Defaults to 2
21
+ attr_reader :write_timeout
22
+
23
+ # Internal:
24
+ # The host this socket is connected to
25
+ attr_reader :host
26
+
27
+ # Internal:
28
+ # The port this socket is connected to
29
+ attr_reader :port
30
+
31
+ # Internal
32
+ #
33
+ # Used for setting TCP_KEEPIDLE: overrides tcp_keepalive_time for a single
34
+ # socket.
35
+ #
36
+ # http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html
37
+ #
38
+ # tcp_keepalive_time:
39
+ #
40
+ # The interval between the last data packet sent (simple ACKs are not
41
+ # considered data) and the first keepalive probe; after the connection is
42
+ # marked to need keepalive, this counter is not used any further.
43
+ attr_reader :keepalive_idle
44
+
45
+ # Internal
46
+ #
47
+ # Used for setting TCP_KEEPINTVL: overrides tcp_keepalive_intvl for a single
48
+ # socket.
49
+ #
50
+ # http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html
51
+ #
52
+ # tcp_keepalive_intvl:
53
+ #
54
+ # The interval between subsequential keepalive probes, regardless of what
55
+ # the connection has exchanged in the meantime.
56
+ attr_reader :keepalive_interval
57
+
58
+ # Internal
59
+ #
60
+ # Used for setting TCP_KEEPCNT: overrides tcp_keepalive_probes for a single
61
+ # socket.
62
+ #
63
+ # http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html
64
+ #
65
+ # tcp_keepalive_probes:
66
+ #
67
+ # The number of unacknowledged probes to send before considering the
68
+ # connection dead and notifying the application layer.
69
+ attr_reader :keepalive_count
70
+
71
+
72
+ # Internal: Create and connect to the given location.
73
+ #
74
+ # options, same as Constructor
75
+ #
76
+ # Returns an instance of KJess::Socket
77
+ def self.connect(options = {})
78
+ s = new(options)
79
+ s.connect
80
+ return s
81
+ end
82
+
83
+ # Internal: Creates a new KJess::Socket
84
+ def initialize( options = {} )
85
+ @host = options[:host]
86
+ @port = options[:port]
87
+
88
+ @connect_timeout = options[:connect_timeout] || options[:timeout] || 2
89
+ @read_timeout = options[:read_timeout] || options[:timeout] || 2
90
+ @write_timeout = options[:write_timeout] || options[:timeout] || 2
91
+
92
+ @keepalive_active = options.fetch(:keepalive_active, true)
93
+ @keepalive_idle = options[:keepalive_idle] || 60
94
+ @keepalive_interval = options[:keepalive_interval] || 30
95
+ @keepalive_count = options[:keepalive_count] || 5
96
+
97
+ @socket = nil
98
+ end
99
+
100
+ # Internal: Return whether or not the keepalive_active flag is set.
101
+ def keepalive_active?
102
+ @keepalive_active
103
+ end
104
+
105
+ # Internal: Low level socket allocation and option configuration
106
+ #
107
+ # Using the options from the initializer, a new ::Socket is created that
108
+ # is:
109
+ #
110
+ # TCP, IPv4 only, autoclosing on exit, nagle's algorithm is disabled and has
111
+ # TCP Keepalive options set if keepalive is supported.
112
+ #
113
+ # Returns a new ::Socket instance
114
+ def blank_socket
115
+ sock = ::Socket.new(::Socket::AF_INET, ::Socket::SOCK_STREAM, 0)
116
+
117
+ # close file descriptors if we exec
118
+ sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
119
+
120
+ # Disable Nagle's algorithm
121
+ sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
122
+
123
+ if using_keepalive? then
124
+ sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE , true)
125
+ sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE , keepalive_idle)
126
+ sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL, keepalive_interval)
127
+ sock.setsockopt(::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT , keepalive_count)
128
+ end
129
+
130
+ return sock
131
+ end
132
+
133
+ # Internal: Return the connected raw Socket.
134
+ #
135
+ # If the socket is closed or non-existent it will create and connect again.
136
+ #
137
+ # Returns a ::Socket
138
+ def socket
139
+ return @socket unless closed?
140
+ @socket ||= connect()
141
+ end
142
+
143
+ # Internal: Closes the internal ::Socket
144
+ #
145
+ # Returns nothing
146
+ def close
147
+ @socket.close unless closed?
148
+ @socket = nil
149
+ end
150
+
151
+ # Internal: Return true the socket is closed.
152
+ def closed?
153
+ return true if @socket.nil?
154
+ return true if @socket.closed?
155
+ return false
156
+ end
157
+
158
+ # Internal:
159
+ #
160
+ # Connect to the remote host in a non-blocking fashion.
161
+ #
162
+ # Raise Error if there is a failure connecting.
163
+ #
164
+ # Return the ::Socket on success
165
+ def connect
166
+ # Calculate our timeout deadline
167
+ deadline = Time.now.to_f + connect_timeout
168
+
169
+ # Lookup destination address, we only want IPv4 , TCP
170
+ addrs = ::Socket.getaddrinfo(host, port, ::Socket::AF_INET, ::Socket::SOCK_STREAM )
171
+ errors = []
172
+ conn_error = lambda { raise errors.first }
173
+ sock = nil
174
+
175
+ addrs.find( conn_error ) do |addr|
176
+ sock = connect_or_error( addr, deadline, errors )
177
+ end
178
+ return sock
179
+ end
180
+
181
+ # Internal: Connect to the destination or raise an error.
182
+ #
183
+ # Connect to the address or capture the error of the connection
184
+ #
185
+ # addr - An address returned from Socket.getaddrinfo()
186
+ # deadline - the after which we should raise a timeout error
187
+ # errors - a collection of errors to append an error too should we have one.
188
+ #
189
+ # Make an attempt to connect to the given address. If it is successful,
190
+ # return the socket.
191
+ #
192
+ # Should the connection fail, append the exception to the errors array and
193
+ # return false.
194
+ #
195
+ def connect_or_error( addr, deadline, errors )
196
+ timeout = deadline - Time.now.to_f
197
+ raise Timeout, "Could not connect to #{host}:#{port}" if timeout <= 0
198
+ return connect_nonblock( addr, timeout )
199
+ rescue Error => e
200
+ errors << e
201
+ return false
202
+ end
203
+
204
+ # Internal: Connect to the give address within the timeout.
205
+ #
206
+ # Make an attempt to connect to a single address within the given timeout.
207
+ #
208
+ # Return the ::Socket when it is connected, or raise an Error if no
209
+ # connection was possible.
210
+ def connect_nonblock( addr, timeout )
211
+ sockaddr = ::Socket.pack_sockaddr_in(addr[1], addr[3])
212
+ sock = blank_socket()
213
+ sock.connect_nonblock( sockaddr )
214
+ return sock
215
+ rescue Errno::EINPROGRESS
216
+ if IO.select(nil, [sock], nil, timeout).nil?
217
+ sock.close rescue nil
218
+ raise Timeout, "Could not connect to #{host}:#{port} within #{timeout} seconds"
219
+ end
220
+ return connect_nonblock_finalize( sock, sockaddr )
221
+ rescue => ex
222
+ sock.close rescue nil
223
+ raise Error, "Could not connect to #{host}:#{port}: #{ex.class}: #{ex.message}", ex.backtrace
224
+ end
225
+
226
+
227
+ # Internal: Make sure that a non-blocking connect has truely connected.
228
+ #
229
+ # Ensure that the given socket is actually connected to the given adddress.
230
+ #
231
+ # Returning the socket if it is and raising an Error if it isn't.
232
+ def connect_nonblock_finalize( sock, sockaddr )
233
+ sock.connect_nonblock( sockaddr )
234
+ return sock
235
+ rescue Errno::EISCONN
236
+ return sock
237
+ rescue => ex
238
+ sock.close rescue nil
239
+ raise Error, "Could not connect to #{host}:#{port}: #{ex.class}: #{ex.message}", ex.backtrace
240
+ end
241
+
242
+ # Internal: say if we are using TCP Keep Alive or not
243
+ #
244
+ # We will return true if the initialization options :keepalive_active is
245
+ # set to true, and if all the constants that are necessary to use TCP keep
246
+ # alive are defined.
247
+ #
248
+ # It may be the case that on some operating systems that the constants are
249
+ # not defined, so in that case we do not want to attempt to use tcp keep
250
+ # alive if we are unable to do so in any case.
251
+ #
252
+ # Returns true or false
253
+ def using_keepalive?
254
+ using = false
255
+ if keepalive_active? then
256
+ using = [ :SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all? do |c|
257
+ ::Socket.const_defined? c
258
+ end
259
+ end
260
+ return using
261
+ end
262
+
263
+ # Reads length bytes from the socket
264
+ #
265
+ # length - the number of bytes to read from the socket
266
+ # outbuf - an optional buffer to store the bytes in
267
+ #
268
+ # Returns the bytes read if no outbuf is specified
269
+ def read(length, outbuf = nil)
270
+ if outbuf
271
+ outbuf.replace('')
272
+ buf = outbuf
273
+ else
274
+ buf = ''
275
+ end
276
+
277
+ while buf.length < length
278
+ unless rb = readpartial(length - buf.length)
279
+ break
280
+ end
281
+
282
+ buf << rb
283
+ end
284
+
285
+ return buf
286
+ end
287
+
288
+ # Internal: Read up to a maxlen of data from the socket and store it in outbuf
289
+ #
290
+ # maxlen - the maximum number of bytes to read from the socket
291
+ # outbuf - the buffer in which to store the bytes.
292
+ #
293
+ # Returns the bytes read
294
+ def readpartial(maxlen, outbuf = nil)
295
+ return socket.read_nonblock(maxlen, outbuf)
296
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNRESET
297
+ if wait_readable(read_timeout)
298
+ retry
299
+ else
300
+ raise Timeout, "Could not read from #{host}:#{port} in #{read_timeout} seconds"
301
+ end
302
+ end
303
+
304
+ # Internal: Write the given data to the socket
305
+ #
306
+ # buf - the data to write to the socket.
307
+ #
308
+ # Raises an error if it is unable to write the data to the socket within the
309
+ # write_timeout.
310
+ #
311
+ # returns nothing
312
+ def write(buf)
313
+ until buf.nil? or (buf.length == 0) do
314
+ written = socket.write_nonblock(buf)
315
+ buf = buf[written, buf.length]
316
+ end
317
+ rescue Errno::EWOULDBLOCK, Errno::EINTR, Errno::EAGAIN, Errno::ECONNRESET
318
+ if wait_writable(write_timeout)
319
+ retry
320
+ else
321
+ raise Timeout, "Could not write to #{host}:#{port} in #{write_timeout} seconds"
322
+ end
323
+ end
324
+
325
+ def wait_writable(timeout = nil)
326
+ IO.select(nil, [@socket], nil, timeout || write_timeout)
327
+ end
328
+
329
+ def wait_readable(timeout = nil)
330
+ IO.select([@socket], nil, nil, timeout || read_timeout)
331
+ end
332
+ end
333
+ end
334
+ end
@@ -1,3 +1,3 @@
1
1
  module Riemann
2
- VERSION = '0.2.2'
2
+ VERSION = '0.2.3'
3
3
  end
metadata CHANGED
@@ -1,62 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riemann-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
5
- prerelease:
4
+ version: 0.2.3
6
5
  platform: ruby
7
6
  authors:
8
7
  - Kyle Kingsbury
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-05-28 00:00:00.000000000 Z
11
+ date: 2014-04-30 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: beefcake
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: 0.3.5
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: 0.3.5
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: trollop
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: 1.16.2
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: 1.16.2
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: mtrc
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: 0.0.4
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: 0.0.4
62
55
  description:
@@ -70,6 +63,7 @@ files:
70
63
  - lib/riemann/auto_state.rb
71
64
  - lib/riemann/client.rb
72
65
  - lib/riemann/client/tcp.rb
66
+ - lib/riemann/client/tcp_socket.rb
73
67
  - lib/riemann/client/udp.rb
74
68
  - lib/riemann/event.rb
75
69
  - lib/riemann/message.rb
@@ -81,26 +75,25 @@ files:
81
75
  - README.markdown
82
76
  homepage: https://github.com/aphyr/riemann-ruby-client
83
77
  licenses: []
78
+ metadata: {}
84
79
  post_install_message:
85
80
  rdoc_options: []
86
81
  require_paths:
87
82
  - lib
88
83
  required_ruby_version: !ruby/object:Gem::Requirement
89
- none: false
90
84
  requirements:
91
- - - ! '>='
85
+ - - '>='
92
86
  - !ruby/object:Gem::Version
93
87
  version: 1.8.7
94
88
  required_rubygems_version: !ruby/object:Gem::Requirement
95
- none: false
96
89
  requirements:
97
- - - ! '>='
90
+ - - '>='
98
91
  - !ruby/object:Gem::Version
99
92
  version: '0'
100
93
  requirements: []
101
94
  rubyforge_project: riemann-client
102
- rubygems_version: 1.8.25
95
+ rubygems_version: 2.0.14
103
96
  signing_key:
104
- specification_version: 3
97
+ specification_version: 4
105
98
  summary: Client for the distributed event system Riemann.
106
99
  test_files: []