logstash-input-relp 2.0.1 → 2.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bf41bf3e9f571773cd7317ccaa639812b8af4b75
4
- data.tar.gz: 5b7b8ade54236ee71f4dd1540a81a831b245c02b
3
+ metadata.gz: 38af08ca585650125a700ef56813a35c18c2f947
4
+ data.tar.gz: ee7e0b9bbc59fa5d1cde1d1aee4fa9240b6f4410
5
5
  SHA512:
6
- metadata.gz: c6724f73a866b45ec55f446c732b1fae0c027fda14bb5cf7516be7fd669a078c82d227db9442fd90467b5ad78d3552873df1ba823573facd37f0807ca17ee548
7
- data.tar.gz: e616c70e39a338199012442b962b2b3766417b028d5b3ab10f0d0aefcd9bdf26e3e970714eed99973865ac286ec02b78504b93b6cbdbf620e1ecc5aba662f27f
6
+ metadata.gz: 86c0e5ea580ebd2dbcaa8a37a76809d5540a3e23cde0a673c2d3936fd348368aaac0d956e0f31a0923f2096fe82940d0b645ce8dc3116f08de10f827a98eb91a
7
+ data.tar.gz: ac4ac922af4bb5de2b03c2b9245c15f9f2f025d5a7146120bb04965b11734cc0d83014105be1c9345add663544ecff33c421dc9830f6bdde861c4f1ae9e86908
@@ -1,3 +1,7 @@
1
+ ## 2.0.2
2
+ - Update the test to play nice within the context of the default
3
+ plugins LS core integration test.
4
+
1
5
  ## 2.0.0
2
6
  - Plugins were updated to follow the new shutdown semantic, this mainly allows Logstash to instruct input plugins to terminate gracefully,
3
7
  instead of using Thread.raise on the plugins' threads. Ref: https://github.com/elastic/logstash/pull/3895
@@ -3,6 +3,7 @@ require "logstash/inputs/base"
3
3
  require "logstash/namespace"
4
4
  require "logstash/util/relp"
5
5
  require "logstash/util/socket_peer"
6
+ require "openssl"
6
7
 
7
8
  # Read RELP events over a TCP socket.
8
9
  #
@@ -47,11 +48,16 @@ class LogStash::Inputs::Relp < LogStash::Inputs::Base
47
48
  def initialize(*args)
48
49
  super(*args)
49
50
  @relp_server = nil
51
+
52
+ # monkey patch TCPSocket and SSLSocket to include socket peer
53
+ TCPSocket.module_eval{include ::LogStash::Util::SocketPeer}
54
+ OpenSSL::SSL::SSLSocket.module_eval{include ::LogStash::Util::SocketPeer}
50
55
  end # def initialize
51
56
 
52
57
  public
53
58
  def register
54
59
  @logger.info("Starting relp input listener", :address => "#{@host}:#{@port}")
60
+
55
61
  if @ssl_enable
56
62
  initialize_ssl_context
57
63
  if @ssl_verify == false
@@ -62,13 +68,17 @@ class LogStash::Inputs::Relp < LogStash::Inputs::Base
62
68
  ].join("\n")
63
69
  end
64
70
  end
71
+
72
+ # note that since we are opening a socket (through @relp_server) in register,
73
+ # we must also make sure we close it in the close method even if we also close
74
+ # it in the stop method since we could have a situation where register is called
75
+ # but not run & stop.
76
+
65
77
  @relp_server = RelpServer.new(@host, @port,['syslog'], @ssl_context)
66
78
  end # def register
67
79
 
68
80
  private
69
81
  def initialize_ssl_context
70
- require "openssl"
71
-
72
82
  @ssl_context = OpenSSL::SSL::SSLContext.new
73
83
  @ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(@ssl_cert))
74
84
  @ssl_context.key = OpenSSL::PKey::RSA.new(File.read(@ssl_key),@ssl_key_passphrase.value)
@@ -110,28 +120,8 @@ class LogStash::Inputs::Relp < LogStash::Inputs::Base
110
120
  def run(output_queue)
111
121
  while !stop?
112
122
  begin
113
- # Start a new thread for each connection.
114
- Thread.start(@relp_server.accept) do |client|
115
- rs = client[0]
116
- socket = client[1]
117
- begin
118
- rs.relp_setup_connection(socket)
119
- # monkeypatch a 'peer' method onto the socket.
120
- socket.instance_eval { class << self; include ::LogStash::Util::SocketPeer end }
121
- peer = socket.peer
122
- @logger.debug("Relp Connection to #{peer} created")
123
- relp_stream(rs,socket, output_queue, peer)
124
- rescue Relp::ConnectionClosed => e
125
- @logger.debug("Relp Connection to #{peer} Closed")
126
- rescue Relp::RelpError => e
127
- @logger.warn('Relp error: '+e.class.to_s+' '+e.message)
128
- #TODO: Still not happy with this, are they all warn level?
129
- #Will this catch everything I want it to?
130
- #Relp spec says to close connection on error, ensure this is the case
131
- ensure
132
- socket.close rescue nil
133
- end
134
- end # Thread.start
123
+ server, socket = @relp_server.accept
124
+ connection_thread(server, socket, output_queue)
135
125
  rescue Relp::InvalidCommand,Relp::InappropriateCommand => e
136
126
  @logger.warn('Relp client trying to open connection with something other than open:'+e.message)
137
127
  rescue Relp::InsufficientCommands
@@ -140,18 +130,58 @@ class LogStash::Inputs::Relp < LogStash::Inputs::Base
140
130
  @logger.debug('Relp Connection closed')
141
131
  rescue OpenSSL::SSL::SSLError => ssle
142
132
  # NOTE(mrichar1): This doesn't return a useful error message for some reason
143
- @logger.error("SSL Error", :exception => ssle,
144
- :backtrace => ssle.backtrace)
145
- end
146
- end # loop
133
+ @logger.error("SSL Error", :exception => ssle, :backtrace => ssle.backtrace)
134
+ rescue => e
135
+ # if this exception occured while the plugin is stopping
136
+ # just ignore and exit
137
+ raise e unless stop?
138
+ end
139
+ end
147
140
  end # def run
148
141
 
149
142
  def stop
143
+ # force close @relp_server which will escape any blocking read with a IO exception
144
+ # and any thread using them will exit.
145
+ # catch all rescue nil to discard any close errors or invalid socket
146
+ if @relp_server
147
+ @relp_server.shutdown rescue nil
148
+ @relp_server = nil
149
+ end
150
+ end
151
+
152
+ def close
153
+ # see related comment in register: we must make sure to close the @relp_server here
154
+ # because it is created in the register method and we could be in the context of having
155
+ # register called but never run & stop, only close.
150
156
  if @relp_server
151
157
  @relp_server.shutdown rescue nil
152
158
  @relp_server = nil
153
159
  end
154
160
  end
161
+
162
+ private
163
+
164
+ def connection_thread(server, socket, output_queue)
165
+ Thread.start(server, socket, output_queue) do |server, socket, output_queue|
166
+ begin
167
+ server.relp_setup_connection(socket)
168
+ peer = socket.peer
169
+ @logger.debug("Relp Connection to #{peer} created")
170
+ relp_stream(server,socket, output_queue, peer)
171
+ rescue Relp::ConnectionClosed => e
172
+ @logger.debug("Relp Connection to #{peer} Closed")
173
+ rescue Relp::RelpError => e
174
+ @logger.warn('Relp error: '+e.class.to_s+' '+e.message)
175
+ #TODO: Still not happy with this, are they all warn level?
176
+ #Will this catch everything I want it to?
177
+ #Relp spec says to close connection on error, ensure this is the case
178
+ rescue => e
179
+ # ignore other exceptions
180
+ ensure
181
+ socket.close rescue nil
182
+ end
183
+ end
184
+ end
155
185
  end # class LogStash::Inputs::Relp
156
186
 
157
187
  #TODO: structured error logging
@@ -15,7 +15,7 @@ class Relp#This isn't much use on its own, but gives RelpServer and RelpClient t
15
15
 
16
16
  def valid_command?(command)
17
17
  valid_commands = Array.new
18
-
18
+
19
19
  #Allow anything in the basic protocol for both directions
20
20
  valid_commands << 'open'
21
21
  valid_commands << 'close'
@@ -102,7 +102,7 @@ class RelpServer < Relp
102
102
 
103
103
  def initialize(host,port,required_commands=[],ssl_context=nil)
104
104
  @logger = Cabin::Channel.get(LogStash)
105
-
105
+
106
106
  @server=true
107
107
 
108
108
  #These are things that are part of the basic protocol, but only valid in one direction (rsp, close etc.)
@@ -197,14 +197,13 @@ class RelpServer < Relp
197
197
  frame['command'] = 'serverclose'
198
198
  begin
199
199
  self.frame_write(socket,frame)
200
- socket.close
200
+ socket.close rescue nil
201
201
  rescue ConnectionClosed
202
202
  end
203
203
  end
204
204
 
205
205
  def shutdown
206
- @server.close
207
- rescue Exception#@server might already be down
206
+ @server.close rescue nil
208
207
  end
209
208
 
210
209
  def ack(socket, txnr)
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-input-relp'
4
- s.version = '2.0.1'
4
+ s.version = '2.0.2'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Read RELP events over a TCP socket."
7
7
  s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
@@ -4,6 +4,8 @@ require_relative "../support/ssl"
4
4
 
5
5
  describe LogStash::Inputs::Relp do
6
6
 
7
+ let!(:helper) { RelpHelpers.new }
8
+
7
9
  before do
8
10
  srand(RSpec.configuration.seed)
9
11
  end
@@ -13,12 +15,11 @@ describe LogStash::Inputs::Relp do
13
15
  it "should register without errors" do
14
16
  input = LogStash::Plugin.lookup("input", "relp").new("port" => 1234)
15
17
  expect {input.register}.to_not raise_error
18
+ input.close rescue nil
16
19
  end
17
-
18
20
  end
19
21
 
20
22
  describe "when interrupting the plugin" do
21
-
22
23
  let(:port) { rand(1024..65532) }
23
24
 
24
25
  it_behaves_like "an interruptible input plugin" do
@@ -28,9 +29,13 @@ describe LogStash::Inputs::Relp do
28
29
 
29
30
  describe "multiple client connections" do
30
31
 
31
- let(:nclients) { rand(200) }
32
+ # (colinsurprenant) don't put number of simultaneous clients too high,
33
+ # it seems to lock no sure why. the test client code needs a very serious,
34
+ # refactoring, its too complex and very hard to debug :P
35
+ let(:nclients) { rand(10) }
36
+
32
37
  let(:nevents) { 100 }
33
- let(:port) { 5512 }
38
+ let(:port) { rand(1024..65532) }
34
39
 
35
40
  let(:conf) do
36
41
  <<-CONFIG
@@ -43,15 +48,22 @@ describe LogStash::Inputs::Relp do
43
48
  CONFIG
44
49
  end
45
50
 
46
- let(:clients) { setup_clients(nclients, port) }
51
+ let(:clients) { RelpHelpers.setup_clients(nclients, port) }
47
52
 
48
53
  let(:events) do
49
- input(conf, (nevents*nclients)) do
54
+ input(conf) do |pipeline, queue|
50
55
  nevents.times do |value|
51
56
  clients.each_with_index do |client, index|
52
57
  client.syslog_write("Hello from client#{index}")
53
58
  end
54
59
  end
60
+ (nevents * nclients).times.collect { queue.pop }
61
+ end
62
+ end
63
+
64
+ after :each do
65
+ clients.each do |client|
66
+ client.close rescue nil
55
67
  end
56
68
  end
57
69
 
@@ -64,50 +76,60 @@ describe LogStash::Inputs::Relp do
64
76
 
65
77
  describe "SSL support" do
66
78
 
67
- let(:nevents) { 100 }
68
79
  let(:certificate) { RelpTest.certificate }
69
- let(:port) { 5513 }
80
+ let(:port) { rand(1024..65532) }
70
81
 
71
- let(:conf) do
72
- <<-CONFIG
73
- input {
74
- relp {
75
- type => "blah"
76
- port => #{port}
77
- ssl_enable => true
78
- ssl_verify => false
79
- ssl_cert => "#{certificate.ssl_cert}"
80
- ssl_key => "#{certificate.ssl_key}"
82
+ context "events reading" do
83
+
84
+ let(:nevents) { 100 }
85
+
86
+ let(:conf) do
87
+ <<-CONFIG
88
+ input {
89
+ relp {
90
+ type => "blah"
91
+ port => #{port}
92
+ ssl_enable => true
93
+ ssl_verify => false
94
+ ssl_cert => "#{certificate.ssl_cert}"
95
+ ssl_key => "#{certificate.ssl_key}"
96
+ }
81
97
  }
82
- }
83
- CONFIG
84
- end
98
+ CONFIG
99
+ end
85
100
 
86
- let(:client) { RelpClient.new("0.0.0.0", port, ["syslog"], {:ssl => true}) }
101
+ let(:client) { RelpClient.new("0.0.0.0", port, ["syslog"], {:ssl => true}) }
87
102
 
88
- let(:events) do
89
- input(conf, nevents) do
90
- nevents.times do
91
- client.syslog_write("Hello from client")
103
+ let!(:events) do
104
+ input(conf) do |pipeline, queue|
105
+ nevents.times do
106
+ client.syslog_write("Hello from client")
107
+ end
108
+ nevents.times.collect { queue.pop }
92
109
  end
93
110
  end
111
+
112
+ after :each do
113
+ client.close rescue nil
114
+ end
115
+
116
+ it "should generated the events as expected" do
117
+ expect(events).to have(nevents).with("Hello from client")
118
+ end
94
119
  end
95
120
 
96
121
  context "registration and close" do
97
122
 
98
123
  it "should register without errors" do
99
- input = LogStash::Plugin.lookup("input", "relp").new("port" => 1235, "ssl_enable" => true,
100
- "ssl_cert" => certificate.ssl_cert,
101
- "ssl_key" => certificate.ssl_key)
124
+ input = LogStash::Plugin.lookup("input", "relp").new(
125
+ "port" => port,
126
+ "ssl_enable" => true,
127
+ "ssl_cert" => certificate.ssl_cert,
128
+ "ssl_key" => certificate.ssl_key
129
+ )
102
130
  expect {input.register}.to_not raise_error
131
+ input.close rescue nil
103
132
  end
104
-
105
133
  end
106
-
107
- it "should generated the events as expected" do
108
- expect(events).to have(nevents).with("Hello from client")
109
- end
110
-
111
134
  end
112
-
113
135
  end
@@ -5,62 +5,26 @@ require "logstash/util/relp"
5
5
  require "socket"
6
6
  require "support/client"
7
7
 
8
- module RelpHelpers
8
+ class RelpHelpers
9
9
 
10
- def setup_clients(number_of_clients, port)
10
+ def self.setup_clients(number_of_clients, port)
11
11
  number_of_clients.times.inject([]) do |clients|
12
12
  clients << RelpClient.new("0.0.0.0", port, ["syslog"])
13
13
  end
14
14
  end
15
15
 
16
- def filter(events, message)
16
+ def self.filter(events, message)
17
17
  events.select{|event| event["message"] == message }
18
18
  end
19
-
20
- def input(config, size, &block)
21
- pipeline = LogStash::Pipeline.new(config)
22
- queue = Queue.new
23
-
24
- pipeline.instance_eval do
25
- # create closure to capture queue
26
- @output_func = lambda { |event| queue << event }
27
-
28
- # output_func is now a method, call closure
29
- def output_func(event)
30
- @output_func.call(event)
31
- end
32
- end
33
-
34
- pipeline_thread = Thread.new { pipeline.run }
35
- sleep 0.1 while !pipeline.ready?
36
-
37
- block.call
38
- sleep 0.1 while queue.size != size
39
-
40
- result = size.times.inject([]) do |acc|
41
- acc << queue.pop
42
- end
43
-
44
- pipeline.shutdown
45
- pipeline_thread.join
46
-
47
- result
48
- end # def input
49
-
50
- end
51
-
52
- RSpec.configure do |c|
53
- c.include RelpHelpers
54
19
  end
55
20
 
56
21
  RSpec::Matchers.define :have do |nevents|
57
22
 
58
23
  match do |events|
59
- filter(events, @pattern).size == nevents
24
+ RelpHelpers.filter(events, @pattern).size == nevents
60
25
  end
61
26
 
62
27
  chain :with do |pattern|
63
28
  @pattern = pattern
64
29
  end
65
-
66
- end
30
+ end
@@ -33,7 +33,7 @@ class RelpClient < Relp
33
33
  end
34
34
  end
35
35
 
36
- #This'll start the automatic frame numbering
36
+ #This'll start the automatic frame numbering
37
37
  @lasttxnr = 0
38
38
 
39
39
  offer=Hash.new
@@ -56,7 +56,7 @@ class RelpClient < Relp
56
56
 
57
57
  #subtracting one array from the other checks to see if all elements in @required_relp_commands are present in the offer
58
58
  elsif ! (@required_relp_commands - response['commands'].split(',')).empty?
59
- #if it can't receive syslog it's useless to us; close the connection
59
+ #if it can't receive syslog it's useless to us; close the connection
60
60
  self.close()
61
61
  raise InsufficientCommands, response['commands'] + ' offered, require '
62
62
  + @required_relp_commands.join(',')
@@ -67,7 +67,12 @@ class RelpClient < Relp
67
67
  #This thread deals with responses that come back
68
68
  reader = Thread.start do
69
69
  loop do
70
- f = self.frame_read(@socket)
70
+ begin
71
+ f = self.frame_read(@socket)
72
+ rescue
73
+ # ignore exceptions and quit thread
74
+ break
75
+ end
71
76
  if f['command'] == 'rsp' && f['message'] == '200 OK'
72
77
  @buffer.delete(f['txnr'])
73
78
  elsif f['command'] == 'rsp' && f['message'][0,1] == '5'
@@ -88,14 +93,19 @@ class RelpClient < Relp
88
93
  Thread.start do
89
94
  old_buffer = Hash.new
90
95
  loop do
91
- #This returns old txnrs that are still present
92
- (@buffer.keys & old_buffer.keys).each do |txnr|
93
- new_txnr = self.frame_write(@socket, @buffer[txnr])
94
- @buffer[new_txnr] = @buffer[txnr]
95
- @buffer.delete(txnr)
96
+ begin
97
+ #This returns old txnrs that are still present
98
+ (@buffer.keys & old_buffer.keys).each do |txnr|
99
+ new_txnr = self.frame_write(@socket, @buffer[txnr])
100
+ @buffer[new_txnr] = @buffer[txnr]
101
+ @buffer.delete(txnr)
102
+ end
103
+ old_buffer = @buffer
104
+ sleep @retransmission_timeout
105
+ rescue
106
+ # ignore exceptions and quit thread
107
+ break
96
108
  end
97
- old_buffer = @buffer
98
- sleep @retransmission_timeout
99
109
  end
100
110
  end
101
111
  end
@@ -107,8 +117,9 @@ class RelpClient < Relp
107
117
  @close_txnr=self.frame_write(@socket, frame)
108
118
  #TODO: ought to properly wait for a reply etc. The serverclose will make it work though
109
119
  sleep @retransmission_timeout
110
- @socket.close#TODO: shutdown?
111
120
  return @buffer
121
+ ensure
122
+ @socket.close
112
123
  end
113
124
 
114
125
  def syslog_write(logline)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-relp
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-08 00:00:00.000000000 Z
11
+ date: 2015-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement