logstash-input-beats 3.1.24-java → 5.1.9-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +111 -14
  3. data/Gemfile +3 -2
  4. data/LICENSE +1 -1
  5. data/VERSION +1 -1
  6. data/docs/index.asciidoc +74 -41
  7. data/lib/logstash/inputs/beats/event_transform_common.rb +1 -0
  8. data/lib/logstash/inputs/beats/message_listener.rb +63 -5
  9. data/lib/logstash/inputs/beats.rb +31 -21
  10. data/lib/logstash-input-beats_jars.rb +9 -9
  11. data/logstash-input-beats.gemspec +2 -2
  12. data/spec/inputs/beats/message_listener_spec.rb +103 -12
  13. data/spec/inputs/beats_spec.rb +15 -26
  14. data/spec/integration/logstash_forwarder_spec.rb +0 -1
  15. data/spec/support/client_process_helpers.rb +6 -4
  16. data/spec/support/helpers.rb +8 -1
  17. data/spec/support/shared_examples.rb +111 -16
  18. data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-annotations/2.9.9/jackson-annotations-2.9.9.jar +0 -0
  19. data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-core/2.9.9/jackson-core-2.9.9.jar +0 -0
  20. data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-databind/2.9.9.3/jackson-databind-2.9.9.3.jar +0 -0
  21. data/vendor/jar-dependencies/com/fasterxml/jackson/module/jackson-module-afterburner/2.9.9/jackson-module-afterburner-2.9.9.jar +0 -0
  22. data/vendor/jar-dependencies/io/netty/netty-all/4.1.30.Final/netty-all-4.1.30.Final.jar +0 -0
  23. data/vendor/jar-dependencies/io/netty/netty-tcnative-boringssl-static/2.0.12.Final/netty-tcnative-boringssl-static-2.0.12.Final.jar +0 -0
  24. data/vendor/jar-dependencies/org/apache/logging/log4j/log4j-api/2.11.1/log4j-api-2.11.1.jar +0 -0
  25. data/vendor/jar-dependencies/org/javassist/javassist/3.24.0-GA/javassist-3.24.0-GA.jar +0 -0
  26. data/vendor/jar-dependencies/org/logstash/beats/logstash-input-beats/5.1.9/logstash-input-beats-5.1.9.jar +0 -0
  27. metadata +22 -14
  28. data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-annotations/2.7.5/jackson-annotations-2.7.5.jar +0 -0
  29. data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-core/2.7.5/jackson-core-2.7.5.jar +0 -0
  30. data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-databind/2.7.5/jackson-databind-2.7.5.jar +0 -0
  31. data/vendor/jar-dependencies/com/fasterxml/jackson/module/jackson-module-afterburner/2.7.5/jackson-module-afterburner-2.7.5.jar +0 -0
  32. data/vendor/jar-dependencies/io/netty/netty-all/4.1.3.Final/netty-all-4.1.3.Final.jar +0 -0
  33. data/vendor/jar-dependencies/io/netty/netty-tcnative-boringssl-static/1.1.33.Fork23/netty-tcnative-boringssl-static-1.1.33.Fork23.jar +0 -0
  34. data/vendor/jar-dependencies/org/apache/logging/log4j/log4j-api/2.6.2/log4j-api-2.6.2.jar +0 -0
  35. data/vendor/jar-dependencies/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar +0 -0
  36. data/vendor/jar-dependencies/org/logstash/beats/logstash-input-beats/3.1.24/logstash-input-beats-3.1.24.jar +0 -0
@@ -2,7 +2,6 @@
2
2
  require "logstash/inputs/base"
3
3
  require "logstash/namespace"
4
4
  require "logstash/timestamp"
5
- require "logstash/codecs/identity_map_codec"
6
5
  require "logstash/codecs/multiline"
7
6
  require "logstash/util"
8
7
  require "logstash-input-beats_jars"
@@ -40,8 +39,7 @@ require_relative "beats/patch"
40
39
  # IMPORTANT: If you are shipping events that span multiple lines, you need to
41
40
  # use the configuration options available in Filebeat to handle multiline events
42
41
  # before sending the event data to Logstash. You cannot use the
43
- # <<plugins-codecs-multiline>> codec to handle multiline events. Doing so may
44
- # result in the mixing of streams and corrupted event data.
42
+ # <<plugins-codecs-multiline>> codec to handle multiline events.
45
43
  #
46
44
  class LogStash::Inputs::Beats < LogStash::Inputs::Base
47
45
  require "logstash/inputs/beats/codec_callback_listener"
@@ -84,6 +82,10 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
84
82
  #
85
83
  config :ssl_certificate_authorities, :validate => :array, :default => []
86
84
 
85
+ # Flag to determine whether to add host information (provided by the beat in the 'hostname' field) to the event
86
+ config :add_hostname, :validate => :boolean, :default => true, :deprecated => 'Host field will not be automatically populated by future version of the Beats input'
87
+
88
+
87
89
  # By default the server doesn't do any client verification.
88
90
  #
89
91
  # `peer` will make the server ask the client to provide a certificate.
@@ -95,6 +97,10 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
95
97
  # This option needs to be used with `ssl_certificate_authorities` and a defined list of CAs.
96
98
  config :ssl_verify_mode, :validate => ["none", "peer", "force_peer"], :default => "none"
97
99
 
100
+ # Enables storing client certificate information in event's metadata. You need
101
+ # to configure the `ssl_verify_mode` to `peer` or `force_peer` to enable this.
102
+ config :ssl_peer_metadata, :validate => :boolean, :default => false
103
+
98
104
  config :include_codec_tag, :validate => :boolean, :default => true
99
105
 
100
106
  # Time in milliseconds for an incomplete ssl handshake to timeout
@@ -102,10 +108,10 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
102
108
 
103
109
  # The number of seconds before we raise a timeout.
104
110
  # This option is useful to control how much time to wait if something is blocking the pipeline.
105
- config :congestion_threshold, :validate => :number, :default => 5, :deprecated => "This option is now deprecated since congestion control is done automatically"
111
+ config :congestion_threshold, :validate => :number, :obsolete => "This option is obsolete since congestion control is done automatically"
106
112
 
107
113
  # This is the default field to which the specified codec will be applied.
108
- config :target_field_for_codec, :validate => :string, :default => "message", :deprecated => "This option is now deprecated, the plugin is now compatible with Filebeat and Logstash-Forwarder"
114
+ config :target_field_for_codec, :validate => :string, :obsolete => "This option is obsolete"
109
115
 
110
116
  # The minimum TLS version allowed for the encrypted connections. The value must be one of the following:
111
117
  # 1.0 for TLS 1.0, 1.1 for TLS 1.1, 1.2 for TLS 1.2
@@ -121,6 +127,9 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
121
127
  # Close Idle clients after X seconds of inactivity.
122
128
  config :client_inactivity_timeout, :validate => :number, :default => 60
123
129
 
130
+ # Beats handler executor thread
131
+ config :executor_threads, :validate => :number, :default => LogStash::Config::CpuCoreStrategy.maximum
132
+
124
133
  def register
125
134
  # For Logstash 2.4 we need to make sure that the logger is correctly set for the
126
135
  # java classes before actually loading them.
@@ -147,26 +156,22 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
147
156
  raise LogStash::ConfigurationError, "Using `verify_mode` set to PEER or FORCE_PEER, requires the configuration of `certificate_authorities`"
148
157
  end
149
158
 
159
+ if client_authentication_metadata? && !require_certificate_authorities?
160
+ raise LogStash::ConfigurationError, "Enabling `peer_metadata` requires using `verify_mode` set to PEER or FORCE_PEER"
161
+ end
162
+
163
+ # Logstash 6.x breaking change (introduced with 4.0.0 of this gem)
150
164
  if @codec.kind_of? LogStash::Codecs::Multiline
151
- @logger.warn("WARNING! - Multiline codec with beats input has been deprecated. Support for this configuration will be removed in a future version. Please refer to the beats documentation for how to best manage multiline data. See https://www.elastic.co/guide/en/beats/filebeat/current/multiline-examples.html")
165
+ raise LogStash::ConfigurationError, "Multiline codec with beats input is not supported. Please refer to the beats documentation for how to best manage multiline data. See https://www.elastic.co/guide/en/beats/filebeat/current/multiline-examples.html"
152
166
  end
153
167
 
154
168
  @logger.info("Beats inputs: Starting input listener", :address => "#{@host}:#{@port}")
155
169
 
156
- # wrap the configured codec to support identity stream
157
- # from the producers if running with the multiline codec.
158
- #
159
- # If they dont need an identity map, codec are stateless and can be reused
160
- # accross multiples connections.
161
- if need_identity_map?
162
- @codec = LogStash::Codecs::IdentityMapCodec.new(@codec)
163
- end
164
-
165
170
  @server = create_server
166
171
  end # def register
167
172
 
168
173
  def create_server
169
- server = org.logstash.beats.Server.new(@host, @port, @client_inactivity_timeout)
174
+ server = org.logstash.beats.Server.new(@host, @port, @client_inactivity_timeout, @executor_threads)
170
175
  if @ssl
171
176
 
172
177
  begin
@@ -182,13 +187,14 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
182
187
  if client_authentification?
183
188
  if @ssl_verify_mode.upcase == "FORCE_PEER"
184
189
  ssl_builder.setVerifyMode(org.logstash.netty.SslSimpleBuilder::SslClientVerifyMode::FORCE_PEER)
190
+ elsif @ssl_verify_mode.upcase == "PEER"
191
+ ssl_builder.setVerifyMode(org.logstash.netty.SslSimpleBuilder::SslClientVerifyMode::VERIFY_PEER)
185
192
  end
186
193
  ssl_builder.setCertificateAuthorities(@ssl_certificate_authorities)
187
194
  end
188
195
 
189
196
  server.enableSSL(ssl_builder)
190
197
  end
191
-
192
198
  server
193
199
  end
194
200
 
@@ -210,14 +216,18 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
210
216
  @server.stop unless @server.nil?
211
217
  end
212
218
 
213
- def need_identity_map?
214
- @codec.kind_of?(LogStash::Codecs::Multiline)
215
- end
216
-
217
219
  def client_authentification?
218
220
  @ssl_certificate_authorities && @ssl_certificate_authorities.size > 0
219
221
  end
220
222
 
223
+ def client_authentication_metadata?
224
+ @ssl_peer_metadata && ssl_configured? && client_authentification?
225
+ end
226
+
227
+ def client_authentication_required?
228
+ @ssl_verify_mode == "force_peer"
229
+ end
230
+
221
231
  def require_certificate_authorities?
222
232
  @ssl_verify_mode == "force_peer" || @ssl_verify_mode == "peer"
223
233
  end
@@ -1,12 +1,12 @@
1
1
  # AUTOGENERATED BY THE GRADLE SCRIPT. DO NOT EDIT.
2
2
 
3
3
  require 'jar_dependencies'
4
- require_jar('io.netty', 'netty-all', '4.1.3.Final')
5
- require_jar('io.netty', 'netty-tcnative-boringssl-static', '1.1.33.Fork23')
6
- require_jar('org.javassist', 'javassist', '3.20.0-GA')
7
- require_jar('com.fasterxml.jackson.core', 'jackson-core', '2.7.5')
8
- require_jar('com.fasterxml.jackson.core', 'jackson-annotations', '2.7.5')
9
- require_jar('com.fasterxml.jackson.core', 'jackson-databind', '2.7.5')
10
- require_jar('com.fasterxml.jackson.module', 'jackson-module-afterburner', '2.7.5')
11
- require_jar('org.apache.logging.log4j', 'log4j-api', '2.6.2')
12
- require_jar('org.logstash.beats', 'logstash-input-beats', '3.1.24')
4
+ require_jar('io.netty', 'netty-all', '4.1.30.Final')
5
+ require_jar('io.netty', 'netty-tcnative-boringssl-static', '2.0.12.Final')
6
+ require_jar('org.javassist', 'javassist', '3.24.0-GA')
7
+ require_jar('com.fasterxml.jackson.core', 'jackson-core', '2.9.9')
8
+ require_jar('com.fasterxml.jackson.core', 'jackson-annotations', '2.9.9')
9
+ require_jar('com.fasterxml.jackson.core', 'jackson-databind', '2.9.9.3')
10
+ require_jar('com.fasterxml.jackson.module', 'jackson-module-afterburner', '2.9.9')
11
+ require_jar('org.apache.logging.log4j', 'log4j-api', '2.11.1')
12
+ require_jar('org.logstash.beats', 'logstash-input-beats', '5.1.9')
@@ -4,7 +4,7 @@ Gem::Specification.new do |s|
4
4
  s.name = "logstash-input-beats"
5
5
  s.version = BEATS_VERSION
6
6
  s.licenses = ["Apache License (2.0)"]
7
- s.summary = "Receive events using the lumberjack protocol."
7
+ s.summary = "Receives events from the Elastic Beats framework"
8
8
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
9
9
  s.authors = ["Elastic"]
10
10
  s.email = "info@elastic.co"
@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
26
26
  s.add_runtime_dependency "concurrent-ruby", "~> 1.0"
27
27
  s.add_runtime_dependency "thread_safe", "~> 0.3.5"
28
28
  s.add_runtime_dependency "logstash-codec-multiline", ">= 2.0.5"
29
- s.add_runtime_dependency 'jar-dependencies', '~> 0.3.4'
29
+ s.add_runtime_dependency 'jar-dependencies', '~> 0.3', '>= 0.3.4'
30
30
 
31
31
  s.add_development_dependency "flores", "~>0.0.6"
32
32
  s.add_development_dependency "rspec"
@@ -6,7 +6,10 @@ require "logstash/inputs/beats/message_listener"
6
6
  require "logstash/instrument/namespaced_null_metric"
7
7
  require "thread"
8
8
 
9
+ java_import java.util.HashMap
10
+
9
11
  class MockMessage
12
+
10
13
  def initialize(identity_stream, data = {})
11
14
  @identity_stream = identity_stream
12
15
  @data = data
@@ -21,6 +24,22 @@ class MockMessage
21
24
  end
22
25
  end
23
26
 
27
+ # General purpose single method mock class. Will keep generating mocks until requested method name (no args) is found.
28
+ class OngoingMethodMock
29
+ def initialize(method_name, return_value)
30
+ @method_name = method_name
31
+ @return_value = return_value
32
+ end
33
+
34
+ def method_missing(method)
35
+ if(method.to_s.eql? @method_name)
36
+ return @return_value
37
+ else
38
+ return OngoingMethodMock.new(@method_name, @return_value)
39
+ end
40
+ end
41
+ end
42
+
24
43
  class DummyCodec < LogStash::Codecs::Base
25
44
  DUMMY_EVENT = LogStash::Event.new
26
45
 
@@ -37,7 +56,11 @@ describe LogStash::Inputs::Beats::MessageListener do
37
56
  let(:queue) { Queue.new }
38
57
  let(:codec) { DummyCodec.new }
39
58
  let(:input) { LogStash::Inputs::Beats.new({ "port" => 5555, "codec" => codec }) }
40
- let(:ctx) { double("ChannelHandlerContext") }
59
+
60
+ let(:ip_address) { "10.0.0.1" }
61
+ let(:remote_address) { OngoingMethodMock.new("getHostAddress", ip_address) }
62
+ let(:ctx) {OngoingMethodMock.new("remoteAddress", remote_address)}
63
+
41
64
  let(:message) { MockMessage.new("abc", { "message" => "hello world"}) }
42
65
 
43
66
  subject { described_class.new(queue, input) }
@@ -47,28 +70,54 @@ describe LogStash::Inputs::Beats::MessageListener do
47
70
  end
48
71
 
49
72
  context "onNewConnection" do
50
- it "register the connection to the connection list" do
51
- expect { subject.onNewConnection(double("ChannelHandlerContext")) }.to change { subject.connections_list.count }.by(1)
73
+ let(:second_ip_address) { "10.0.0.2" }
74
+ let(:second_ctx) {OngoingMethodMock.new("getHostAddress", second_ip_address)}
75
+
76
+ shared_examples 'a valid new connection' do
77
+ it "register the connection to the connection list" do
78
+ expect { subject.onNewConnection(second_ctx) }.to change { subject.connections_list.count }.by(1)
79
+ end
80
+ end
81
+
82
+ it_behaves_like 'a valid new connection'
83
+
84
+ context 'with a nil remote address' do
85
+ let(:second_ip_address) { nil}
86
+
87
+ it_behaves_like 'a valid new connection'
88
+ end
89
+
90
+ context 'when the channel throws retrieving remote address' do
91
+ before do
92
+ allow(second_ctx).to receive(:channel).and_raise('nope')
93
+ end
94
+
95
+ it_behaves_like 'a valid new connection'
52
96
  end
53
97
 
54
98
  describe "metrics" do
55
99
  it "new connection should increment connection count" do
56
100
  expect(subject).to receive(:increment_connection_count).once
57
- subject.onNewConnection(double("ChannelHandlerContext"))
101
+ subject.onNewConnection(second_ctx)
58
102
  end
59
103
 
60
104
  describe "peak connections" do
105
+ let (:ctxes) { [1, 2, 3, 4].inject([]) do |result, element|
106
+ result << OngoingMethodMock.new("getHostAddress", "10.0.0.#{element}")
107
+ result
108
+ end
109
+ }
61
110
  it "closing and open connections should keep highest count" do
62
111
  expect(subject.instance_eval("@peak_connection_count").value).to eq(1)
63
- subject.onNewConnection(1)
112
+ subject.onNewConnection(ctxes[0])
64
113
  expect(subject.instance_eval("@peak_connection_count").value).to eq(2)
65
- subject.onNewConnection(2)
114
+ subject.onNewConnection(ctxes[1])
66
115
  expect(subject.instance_eval("@peak_connection_count").value).to eq(3)
67
- subject.onConnectionClose(2)
116
+ subject.onConnectionClose(ctxes[1])
68
117
  expect(subject.instance_eval("@peak_connection_count").value).to eq(3)
69
- subject.onNewConnection(3)
118
+ subject.onNewConnection(ctxes[2])
70
119
  expect(subject.instance_eval("@peak_connection_count").value).to eq(3)
71
- subject.onNewConnection(4)
120
+ subject.onNewConnection(ctxes[3])
72
121
  expect(subject.instance_eval("@peak_connection_count").value).to eq(4)
73
122
  end
74
123
  end
@@ -78,7 +127,7 @@ describe LogStash::Inputs::Beats::MessageListener do
78
127
 
79
128
  context "onNewMessage" do
80
129
  context "when the message is from filebeat" do
81
- let(:message) { MockMessage.new("abc", { "message" => "hello world" } )}
130
+ let(:message) { MockMessage.new("abc", { "message" => "hello world", "@metadata" => {} } )}
82
131
 
83
132
  it "extract the event" do
84
133
  subject.onNewMessage(ctx, message)
@@ -88,7 +137,7 @@ describe LogStash::Inputs::Beats::MessageListener do
88
137
  end
89
138
 
90
139
  context "when the message is from LSF" do
91
- let(:message) { MockMessage.new("abc", { "line" => "hello world" } )}
140
+ let(:message) { MockMessage.new("abc", { "line" => "hello world", '@metadata' => {} } )}
92
141
 
93
142
  it "extract the event" do
94
143
  subject.onNewMessage(ctx, message)
@@ -98,7 +147,16 @@ describe LogStash::Inputs::Beats::MessageListener do
98
147
  end
99
148
 
100
149
  context "when the message is from any libbeat" do
101
- let(:message) { MockMessage.new("abc", { "metric" => 1, "name" => "super-stats"} )}
150
+ #Requires data modeled as Java, not Ruby since the actual code pulls from Java backed (Netty) object
151
+ let(:data) do
152
+ d = HashMap.new
153
+ d.put('@metadata', HashMap.new)
154
+ d.put('metric', 1)
155
+ d.put('name', "super-stats")
156
+ d
157
+ end
158
+
159
+ let(:message) { MockMessage.new("abc", data)}
102
160
 
103
161
  it "extract the event" do
104
162
  subject.onNewMessage(ctx, message)
@@ -106,7 +164,40 @@ describe LogStash::Inputs::Beats::MessageListener do
106
164
  expect(event.get("message")).to be_nil
107
165
  expect(event.get("metric")).to eq(1)
108
166
  expect(event.get("name")).to eq("super-stats")
167
+ expect(event.get("[@metadata][ip_address]")).to eq(ip_address)
109
168
  end
169
+
170
+ context 'when the remote address is nil' do
171
+ let(:ctx) { OngoingMethodMock.new("remoteAddress", nil)}
172
+
173
+ it 'extracts the event' do
174
+ subject.onNewMessage(ctx, message)
175
+ event = queue.pop
176
+ expect(event.get("message")).to be_nil
177
+ expect(event.get("metric")).to eq(1)
178
+ expect(event.get("name")).to eq("super-stats")
179
+ expect(event.get("[@metadata][ip_address]")).to eq(nil)
180
+ end
181
+ end
182
+
183
+ context 'when getting the remote address raises' do
184
+ let(:raising_ctx) { double("context")}
185
+
186
+ before do
187
+ allow(raising_ctx).to receive(:channel).and_raise("nope")
188
+ subject.onNewConnection(raising_ctx)
189
+ end
190
+
191
+ it 'extracts the event' do
192
+ subject.onNewMessage(raising_ctx, message)
193
+ event = queue.pop
194
+ expect(event.get("message")).to be_nil
195
+ expect(event.get("metric")).to eq(1)
196
+ expect(event.get("name")).to eq("super-stats")
197
+ expect(event.get("[@metadata][ip_address]")).to eq(nil)
198
+ end
199
+ end
200
+
110
201
  end
111
202
  end
112
203
 
@@ -16,43 +16,20 @@ describe LogStash::Inputs::Beats do
16
16
 
17
17
  context "#register" do
18
18
  context "host related configuration" do
19
- let(:config) { super.merge!({ "host" => host, "port" => port, "client_inactivity_timeout" => client_inactivity_timeout }) }
19
+ let(:config) { super.merge!({ "host" => host, "port" => port, "client_inactivity_timeout" => client_inactivity_timeout, "executor_threads" => threads }) }
20
20
  let(:host) { "192.168.1.20" }
21
21
  let(:port) { 9000 }
22
22
  let(:client_inactivity_timeout) { 400 }
23
+ let(:threads) { 10 }
23
24
 
24
25
  subject(:plugin) { LogStash::Inputs::Beats.new(config) }
25
26
 
26
27
  it "sends the required options to the server" do
27
- expect(org.logstash.beats.Server).to receive(:new).with(host, port, client_inactivity_timeout)
28
+ expect(org.logstash.beats.Server).to receive(:new).with(host, port, client_inactivity_timeout, threads)
28
29
  subject.register
29
30
  end
30
31
  end
31
32
 
32
- context "identity map" do
33
- subject(:plugin) { LogStash::Inputs::Beats.new(config) }
34
- before { plugin.register }
35
-
36
- context "when using the multiline codec" do
37
- let(:codec) { LogStash::Codecs::Multiline.new("pattern" => '^2015',
38
- "what" => "previous",
39
- "negate" => true) }
40
- let(:config) { super.merge({ "codec" => codec }) }
41
-
42
- it "wraps the codec with the identity_map" do
43
- expect(plugin.codec).to be_kind_of(LogStash::Codecs::IdentityMapCodec)
44
- end
45
- end
46
-
47
- context "when using non buffered codecs" do
48
- let(:config) { super.merge({ "codec" => "json" }) }
49
-
50
- it "doesnt wrap the codec with the identity map" do
51
- expect(plugin.codec).to be_kind_of(LogStash::Codecs::JSON)
52
- end
53
- end
54
- end
55
-
56
33
  it "raise no exception" do
57
34
  plugin = LogStash::Inputs::Beats.new(config)
58
35
  expect { plugin.register }.not_to raise_error
@@ -146,6 +123,18 @@ describe LogStash::Inputs::Beats do
146
123
  end
147
124
  end
148
125
  end
126
+
127
+ context "with multiline codec" do
128
+ let(:codec) { LogStash::Codecs::Multiline.new("pattern" => '^2015',
129
+ "what" => "previous",
130
+ "negate" => true) }
131
+ let(:config) { super.merge({ "codec" => codec }) }
132
+
133
+ it "raise a ConfigurationError when multiline codec is set" do
134
+ plugin = LogStash::Inputs::Beats.new(config)
135
+ expect {plugin.register}.to raise_error(LogStash::ConfigurationError, "Multiline codec with beats input is not supported. Please refer to the beats documentation for how to best manage multiline data. See https://www.elastic.co/guide/en/beats/filebeat/current/multiline-examples.html")
136
+ end
137
+ end
149
138
  end
150
139
 
151
140
  context "when interrupting the plugin" do
@@ -55,7 +55,6 @@ describe "Logstash-Forwarder", :integration => true do
55
55
  f.write(events.join("\n") + "\n")
56
56
  end
57
57
  sleep(1) # give some time to the clients to pick up the changes
58
- stop_client
59
58
  end
60
59
 
61
60
  after :each do
@@ -32,10 +32,12 @@ module ClientProcessHelpers
32
32
  end
33
33
 
34
34
  def stop_client
35
- begin
36
- @process.poll_for_exit(5)
37
- rescue ChildProcess::TimeoutError
38
- Process.kill("KILL", @process.pid)
35
+ unless @process.nil?
36
+ begin
37
+ @process.poll_for_exit(5)
38
+ rescue ChildProcess::TimeoutError
39
+ Process.kill("KILL", @process.pid)
40
+ end
39
41
  end
40
42
  end
41
43
  end
@@ -11,6 +11,13 @@ OPEN_SSL_TOPK8 = "openssl pkcs8 -nocrypt -topk8 -inform PEM -outform PEM"
11
11
  #
12
12
  def convert_to_pkcs8(key)
13
13
  out, e, s = Open3.capture3(OPEN_SSL_TOPK8, :stdin_data => key.to_s)
14
- raise e if e != ""
14
+ # attempt to address random failures by trying again
15
+ unless s.success?
16
+ sleep 1
17
+ out, e, s = Open3.capture3(OPEN_SSL_TOPK8, :stdin_data => key.to_s)
18
+ raise e if e != ""
19
+ out
20
+ end
21
+
15
22
  out
16
23
  end
@@ -25,31 +25,126 @@ shared_examples "Common Event Transformation" do
25
25
  expect(subject.get("tags")).to include(tag)
26
26
  end
27
27
 
28
- context "when the `beats.hostname` doesnt exist on the event" do
29
- let(:already_exist) { "already_exist" }
30
- let(:event_map) { super.merge({ "host" => already_exist }) }
28
+ context 'when add_hostname is true' do
29
+ let(:config) { super.merge({'add_hostname' => true})}
31
30
 
32
- it "doesnt change the value" do
33
- expect(subject.get("host")).to eq(already_exist)
31
+ context 'when a host is provided in beat.host.name' do
32
+ let(:already_exist) { "already_exist" }
33
+ let(:producer_host) { "newhost01" }
34
+ let(:event_map) { super.merge({ "beat" => { "host" => {"name" => producer_host }}}) }
35
+
36
+ context "when no `host` key already exists on the event" do
37
+ it "does not set the host value" do
38
+ expect(subject.get("host")).to be_nil
39
+ end
40
+ end
41
+
42
+ context "when `host` key exists on the event" do
43
+ let(:already_exist) { "already_exist" }
44
+ let(:event_map) { super.merge({ "host" => already_exist }) }
45
+
46
+ it "doesn't override it" do
47
+ expect(subject.get("host")).to eq(already_exist)
48
+ end
49
+ end
34
50
  end
35
- end
36
51
 
37
- context "when the `beat.hostname` exist in the event" do
38
- let(:producer_host) { "newhost01" }
39
- let(:event_map) { super.merge({ "beat" => { "hostname" => producer_host }}) }
52
+ context "when a host is set in `beat.hostname`" do
53
+ let(:producer_host) { "newhost01" }
54
+ let(:event_map) { super.merge({ "beat" => { "hostname" => producer_host }}) }
40
55
 
41
- context "when `host` key doesn't exist on the event" do
42
- it "copy the `beat.hostname` to `host` or backward compatibility" do
43
- expect(subject.get("host")).to eq(producer_host)
56
+ context "when no `host` key already exists on the event" do
57
+ it "copies the value in `beat.hostname` to `host`" do
58
+ expect(subject.get("host")).to eq(producer_host)
59
+ end
60
+ end
61
+
62
+ context "when `host` key exists on the event" do
63
+ let(:already_exist) { "already_exist" }
64
+ let(:event_map) { super.merge({ "host" => already_exist }) }
65
+
66
+ it "doesn't override it" do
67
+ expect(subject.get("host")).to eq(already_exist)
68
+ end
69
+ end
70
+ end
71
+
72
+ context "when no host is provided in beat" do
73
+ context "when no `host` key already exists on the event" do
74
+ it "does not set the host" do
75
+ expect(subject.get("host")).to be_nil
76
+ end
77
+ end
78
+
79
+ context "when `host` key already exists on the event" do
80
+ let(:already_exist) { "already_exist" }
81
+ let(:event_map) { super.merge({ "host" => already_exist }) }
82
+
83
+ it "doesn't override it" do
84
+ expect(subject.get("host")).to eq(already_exist)
85
+ end
44
86
  end
45
87
  end
88
+ end
46
89
 
47
- context "when `host` key exists on the event" do
90
+ context 'when add hostname is false' do
91
+ let(:config) { super.merge({'add_hostname' => false})}
92
+
93
+ context 'when a host is provided in beat.host.name' do
48
94
  let(:already_exist) { "already_exist" }
49
- let(:event_map) { super.merge({ "host" => already_exist }) }
95
+ let(:producer_host) { "newhost01" }
96
+ let(:event_map) { super.merge({ "beat" => { "host" => {"name" => producer_host }}}) }
97
+
98
+ context "when no `host` key already exists on the event" do
99
+ it "does not set the host" do
100
+ expect(subject.get("host")).to be_nil
101
+ end
102
+ end
103
+
104
+ context "when `host` key already exists on the event" do
105
+ let(:already_exist) { "already_exist" }
106
+ let(:event_map) { super.merge({ "host" => already_exist }) }
107
+
108
+ it "doesn't override it" do
109
+ expect(subject.get("host")).to eq(already_exist)
110
+ end
111
+ end
112
+ end
113
+
114
+ context "when a host is provided in `beat.hostname`" do
115
+ let(:producer_host) { "newhost01" }
116
+ let(:event_map) { super.merge({ "beat" => { "hostname" => producer_host }}) }
117
+
118
+ context "when no `host` key already exists on the event" do
119
+ it "does not set the host" do
120
+ expect(subject.get("host")).to be_nil
121
+ end
122
+ end
123
+
124
+ context "when `host` key already exists on the event" do
125
+ let(:already_exist) { "already_exist" }
126
+ let(:event_map) { super.merge({ "host" => already_exist }) }
127
+
128
+ it "doesn't override it" do
129
+ expect(subject.get("host")).to eq(already_exist)
130
+ end
131
+ end
132
+ end
133
+
134
+ context "when no host is provided in beat" do
135
+ context "when no `host` key already exists on the event" do
136
+ it "does not set the host" do
137
+ expect(subject.get("host")).to be_nil
138
+ end
139
+ end
140
+
141
+ context "when `host` key already exists on the event" do
142
+ let(:already_exist) { "already_exist" }
143
+ let(:event_map) { super.merge({ "host" => already_exist }) }
50
144
 
51
- it "doesn't override it" do
52
- expect(subject.get("host")).to eq(already_exist)
145
+ it "doesn't override it" do
146
+ expect(subject.get("host")).to eq(already_exist)
147
+ end
53
148
  end
54
149
  end
55
150
  end