logstash-input-beats 2.0.3 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,9 +1,9 @@
1
- require_relative "../spec_helper"
2
- require "logstash/circuit_breaker"
1
+ require_relative "../../spec_helper"
2
+ require "logstash/inputs/beats_support/circuit_breaker"
3
3
 
4
4
  class DummyErrorTest < StandardError; end
5
5
 
6
- describe LogStash::CircuitBreaker do
6
+ describe LogStash::Inputs::BeatsSupport::CircuitBreaker do
7
7
  let(:error_threshold) { 1 }
8
8
  let(:options) do
9
9
  {
@@ -12,7 +12,7 @@ describe LogStash::CircuitBreaker do
12
12
  }
13
13
  end
14
14
 
15
- subject { LogStash::CircuitBreaker.new("testing", options) }
15
+ subject { described_class.new("testing", options) }
16
16
 
17
17
  context "when the breaker is closed" do
18
18
  it "closed by default" do
@@ -24,7 +24,7 @@ describe LogStash::CircuitBreaker do
24
24
  subject.execute do
25
25
  raise DummyErrorTest
26
26
  end
27
- }.to raise_error(LogStash::CircuitBreaker::HalfOpenBreaker)
27
+ }.to raise_error(LogStash::Inputs::BeatsSupport::CircuitBreaker::HalfOpenBreaker)
28
28
  end
29
29
 
30
30
  it "open if we pass the errors threadshold" do
@@ -32,18 +32,19 @@ describe LogStash::CircuitBreaker do
32
32
  subject.execute do
33
33
  raise DummyErrorTest
34
34
  end
35
- }.to raise_error(LogStash::CircuitBreaker::HalfOpenBreaker)
35
+ }.to raise_error(LogStash::Inputs::BeatsSupport::CircuitBreaker::HalfOpenBreaker)
36
36
 
37
37
  expect {
38
38
  subject.execute do
39
39
  raise DummyErrorTest
40
40
  end
41
- }.to raise_error(LogStash::CircuitBreaker::OpenBreaker)
41
+ }.to raise_error(LogStash::Inputs::BeatsSupport::CircuitBreaker::OpenBreaker)
42
42
  end
43
43
  end
44
44
 
45
45
  context "When the breaker is open" do
46
- let(:future_time) { Time.now + 3600 }
46
+ let(:retry_time) { 2 }
47
+ let(:options) { super.merge(:time_before_retry => retry_time) }
47
48
 
48
49
  before do
49
50
  # trip the breaker
@@ -62,7 +63,7 @@ describe LogStash::CircuitBreaker do
62
63
  end
63
64
 
64
65
  it "resets the breaker after the time before retry" do
65
- expect(Time).to receive(:now).at_least(2).and_return(future_time)
66
+ sleep(retry_time + 1)
66
67
  expect(subject.closed?).to eq(true)
67
68
  end
68
69
 
@@ -73,7 +74,7 @@ describe LogStash::CircuitBreaker do
73
74
  subject.execute do
74
75
  runned = true
75
76
  end
76
- rescue LogStash::CircuitBreaker::OpenBreaker
77
+ rescue LogStash::Inputs::BeatsSupport::CircuitBreaker::OpenBreaker
77
78
  end
78
79
 
79
80
  expect(runned).to eq(false)
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+ require "logstash/inputs/beats"
3
+ require "logstash/event"
4
+ require "spec_helper"
5
+ require "thread"
6
+
7
+ Thread.abort_on_exception = true
8
+ describe LogStash::Inputs::BeatsSupport::CodecCallbackListener do
9
+ let(:data) { "Hello world" }
10
+ let(:map) do
11
+ {
12
+ "beat" => { "hostname" => "newhost" }
13
+ }
14
+ end
15
+ let(:path) { "/var/log/message" }
16
+ let(:transformer) { double("codec_transformer") }
17
+ let(:queue_timeout) { 1 }
18
+ let(:queue) { LogStash::Inputs::BeatsSupport::SynchronousQueueWithOffer.new(queue_timeout) }
19
+ let(:event) { LogStash::Event.new }
20
+
21
+ before do
22
+ allow(transformer).to receive(:transform).with(event, map).and_return(event)
23
+ end
24
+
25
+ subject { described_class.new(data, map, path, transformer, queue) }
26
+
27
+ it "expose the data" do
28
+ expect(subject.data).to eq(data)
29
+ end
30
+
31
+ it "expose the path" do
32
+ expect(subject.path).to eq(path)
33
+ end
34
+
35
+ context "when the queue is not blocked for too long" do
36
+ let(:queue_timeout) { 5 }
37
+
38
+ it "doesnt raise an exception" do
39
+ Thread.new do
40
+ loop { queue.take }
41
+ end
42
+ sleep(0.1)
43
+ expect { subject.process_event(event) }.not_to raise_error
44
+ end
45
+ end
46
+
47
+ context "when the queue is blocked for too long" do
48
+ it "raises an exception" do
49
+ expect { subject.process_event(event) }.to raise_error(LogStash::Inputs::Beats::InsertingToQueueTakeTooLong)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,93 @@
1
+ # encoding: utf-8
2
+ require "logstash/inputs/beats"
3
+ require_relative "../../support/logstash_test"
4
+ require "spec_helper"
5
+
6
+ describe LogStash::Inputs::BeatsSupport::ConnectionHandler do
7
+ let(:config) do
8
+ {
9
+ "port" => 0,
10
+ "type" => "example",
11
+ "tags" => "beats"
12
+ }
13
+ end
14
+
15
+ let(:logger) { DummyLogger.new }
16
+ let(:input) do
17
+ LogStash::Inputs::Beats.new(config).tap do |i|
18
+ i.register
19
+ end
20
+ end
21
+ let(:connection) { double("connection") }
22
+ let(:queue) { DummyNeverBlockedQueue.new }
23
+
24
+ subject { described_class.new(connection, input, queue) }
25
+
26
+ context "#accept" do
27
+ let(:connection) { DummyConnection.new(events) }
28
+ let(:events) {
29
+ [
30
+ { :map => { "id" => 1 }, :identity_stream => "/var/log/message" },
31
+ { :map => { "id" => 2 }, :identity_stream => "/var/log/message_2" }
32
+ ]
33
+ }
34
+ it "should delegate work to `#process` for each items" do
35
+ events.each do |element|
36
+ expect(subject).to receive(:process).with(element[:map], element[:identity_stream]).and_return(true)
37
+ end
38
+
39
+ subject.accept
40
+ end
41
+ end
42
+
43
+ context "#process" do
44
+ let(:identity_stream) { "/var/log/message" }
45
+
46
+ context "without a `target_field_for_codec`" do
47
+ let(:map) { { "hello" => "world" } }
48
+
49
+ context "queue is not blocked" do
50
+ it "insert the event into the queue" do
51
+ subject.process(map, identity_stream)
52
+ event = queue.take
53
+
54
+ expect(event["hello"]).to eq(map["hello"])
55
+ expect(event["tags"]).to include("beats_input_raw_event")
56
+ end
57
+ end
58
+
59
+ context "queue is blocked" do
60
+ let(:queue_timeout) { 1 }
61
+ let(:queue) { LogStash::Inputs::BeatsSupport::SynchronousQueueWithOffer.new(queue_timeout) }
62
+
63
+ it "raise an exception" do
64
+ expect { subject.process(map, identity_stream) }.to raise_error(LogStash::Inputs::Beats::InsertingToQueueTakeTooLong)
65
+ end
66
+ end
67
+ end
68
+
69
+ context "with a codec" do
70
+ let(:message) { "Hello world" }
71
+ let(:map) { { "message" => message } }
72
+
73
+ context "queue is not blocked" do
74
+ it "insert the event into the queue" do
75
+ subject.process(map, identity_stream)
76
+ event = queue.take
77
+
78
+ expect(event["message"]).to eq(message)
79
+ expect(event["tags"]).to include("beats_input_codec_plain_applied")
80
+ end
81
+ end
82
+
83
+ context "queue is blocked" do
84
+ let(:queue_timeout) { 1 }
85
+ let(:queue) { LogStash::Inputs::BeatsSupport::SynchronousQueueWithOffer.new(queue_timeout) }
86
+
87
+ it "raise an exception" do
88
+ expect { subject.process(map, identity_stream) }.to raise_error(LogStash::Inputs::Beats::InsertingToQueueTakeTooLong)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,67 @@
1
+ # encoding: utf-8
2
+ require "logstash/event"
3
+ require "logstash/timestamp"
4
+ require "logstash/inputs/beats"
5
+ require_relative "../../support/shared_examples"
6
+ require "spec_helper"
7
+
8
+ describe LogStash::Inputs::BeatsSupport::DecodedEventTransform do
9
+ let(:config) do
10
+ {
11
+ "port" => 0,
12
+ "type" => "example",
13
+ "tags" => "beats"
14
+ }
15
+ end
16
+
17
+ let(:input) do
18
+ LogStash::Inputs::Beats.new(config).tap do |i|
19
+ i.register
20
+ end
21
+ end
22
+ let(:event) { LogStash::Event.new }
23
+ let(:map) do
24
+ {
25
+ "@metadata" => { "hello" => "world"},
26
+ "super" => "mario"
27
+ }
28
+ end
29
+
30
+ subject { described_class.new(input).transform(event, map) }
31
+
32
+ include_examples "Common Event Transformation"
33
+
34
+ it "tags the event" do
35
+ expect(subject["tags"]).to include("beats_input_codec_plain_applied")
36
+ end
37
+
38
+ it "merges the other data from the map to the event" do
39
+ expect(subject["super"]).to eq(map["super"])
40
+ expect(subject["@metadata"]).to include(map["@metadata"])
41
+ end
42
+
43
+ context "map contains a timestamp" do
44
+ context "when its valid" do
45
+ let(:timestamp) { Time.now }
46
+ let(:map) { super.merge({"@timestamp" => timestamp }) }
47
+
48
+ it "uses as the event timestamp" do
49
+ expect(subject["@timestamp"]).to eq(LogStash::Timestamp.coerce(timestamp))
50
+ end
51
+ end
52
+
53
+ context "when its not valid" do
54
+ let(:map) { super.merge({"@timestamp" => "invalid" }) }
55
+
56
+ it "fallback the current time" do
57
+ expect(subject["@timestamp"]).to be_kind_of(LogStash::Timestamp)
58
+ end
59
+ end
60
+ end
61
+
62
+ context "when the map doesn't provide a timestamp" do
63
+ it "fallback the current time" do
64
+ expect(subject["@timestamp"]).to be_kind_of(LogStash::Timestamp)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+ require "logstash/event"
3
+ require "logstash/inputs/beats"
4
+ require_relative "../../support/shared_examples"
5
+ require "spec_helper"
6
+
7
+ describe LogStash::Inputs::BeatsSupport::EventTransformCommon do
8
+ subject { described_class.new(input).transform(event) }
9
+
10
+ include_examples "Common Event Transformation"
11
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ require "logstash/event"
3
+ require "logstash/inputs/beats"
4
+ require_relative "../../support/shared_examples"
5
+ require "spec_helper"
6
+
7
+ describe LogStash::Inputs::BeatsSupport::RawEventTransform do
8
+ let(:config) do
9
+ {
10
+ "port" => 0,
11
+ "type" => "example",
12
+ "tags" => "beats"
13
+ }
14
+ end
15
+
16
+ let(:input) { LogStash::Inputs::Beats.new(config) }
17
+ let(:event) { LogStash::Event.new }
18
+
19
+ subject { described_class.new(input).transform(event) }
20
+
21
+ include_examples "Common Event Transformation"
22
+
23
+ it "tags the event" do
24
+ expect(subject["tags"]).to include("beats_input_raw_event")
25
+ end
26
+ end
@@ -17,7 +17,7 @@ describe "A client" do
17
17
  let(:host) { "127.0.0.1" }
18
18
  let(:queue) { [] }
19
19
 
20
- before do
20
+ before :each do
21
21
  expect(File).to receive(:read).at_least(1).with(certificate_file_crt) { certificate.first.to_s }
22
22
  expect(File).to receive(:read).at_least(1).with(certificate_file_key) { certificate.last.to_s }
23
23
 
@@ -29,23 +29,33 @@ describe "A client" do
29
29
  :ssl_key => certificate_file_key)
30
30
 
31
31
  @tcp_server = Thread.new do
32
+ tcp_server.run { |data, identity_stream| queue << [data, identity_stream] }
32
33
  while true
33
- tcp_server.accept do |socket|
34
- con = Lumberjack::Beats::Connection.new(socket, tcp_server)
35
- begin
36
- con.run { |data, identity_stream| queue << [data, identity_stream] }
37
- rescue
38
- # Close connection on failure. For example SSL client will make
39
- # parser for TCP based server trip.
40
- # Connection is closed by Server connection object
41
- end
42
- end
34
+ tcp_server.accept do |socket|
35
+ next if socket.nil?
36
+
37
+ begin
38
+ con = Lumberjack::Beats::Connection.new(socket, tcp_server)
39
+ con.run { |data, identity_stream| queue << [data, identity_stream] }
40
+ rescue
41
+ # Close connection on failure. For example SSL client will make
42
+ # parser for TCP based server trip.
43
+ # Connection is closed by Server connection object
44
+ end
45
+ end
43
46
  end
44
47
  end
45
48
 
46
49
  @ssl_server = Thread.new do
47
50
  ssl_server.run { |data, identity_stream| queue << [data, identity_stream] }
48
51
  end
52
+
53
+ sleep(0.1) while @ssl_server.status != "run" && @tcp_server != "run"
54
+ end
55
+
56
+ after :each do
57
+ @tcp_server.kill
58
+ @ssl_server.kill
49
59
  end
50
60
 
51
61
  shared_examples "send payload" do
@@ -132,7 +142,7 @@ describe "A client" do
132
142
  :addresses => host,
133
143
  :ssl => false)
134
144
  client.write({ "line" => "foobar" })
135
- }.to raise_error(RuntimeError)
145
+ }.to raise_error
136
146
  end
137
147
 
138
148
  context "When transmitting a payload" do
@@ -32,9 +32,9 @@ describe "Server" do
32
32
  end
33
33
  end
34
34
 
35
- sleep(1) while thread.status != "run"
35
+ sleep(0.1) while thread.status != "run"
36
36
  server.close
37
- sleep(2)
38
- expect(thread.status).to be_falsey
37
+
38
+ wait_for { thread.status }.to be_falsey
39
39
  end
40
40
  end
@@ -21,3 +21,28 @@ module LogStashTest
21
21
  end
22
22
  end
23
23
  end
24
+
25
+ class DummyNeverBlockedQueue < Array
26
+ def offer(element, timeout = nil)
27
+ push(element)
28
+ end
29
+
30
+ alias_method :take, :shift
31
+ end
32
+
33
+ class DummyConnection
34
+ def initialize(events)
35
+ @events = events
36
+ end
37
+
38
+ def run
39
+ @events.each do |element|
40
+ yield element[:map], element[:identity_stream]
41
+ end
42
+ end
43
+
44
+ def peer
45
+ "localhost:5555"
46
+ end
47
+ end
48
+
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+ shared_examples "Common Event Transformation" do
3
+ let(:tag) { "140-rpm-beats" }
4
+ let(:config) do
5
+ {
6
+ "port" => 0,
7
+ "type" => "example",
8
+ "tags" => tag
9
+ }
10
+ end
11
+
12
+ let(:input) do
13
+ LogStash::Inputs::Beats.new(config).tap do |i|
14
+ i.register
15
+ end
16
+ end
17
+ let(:event) { LogStash::Event.new(event_map) }
18
+ let(:event_map) do
19
+ {
20
+ "message" => "Hello world",
21
+ }
22
+ end
23
+
24
+ it "adds configured tags to the event" do
25
+ expect(subject["tags"]).to include(tag)
26
+ end
27
+
28
+ context "when the `beast.hotname` doesnt exist on the event" do
29
+ let(:already_exist) { "already_exist" }
30
+ let(:event_map) { super.merge({ "host" => already_exist }) }
31
+
32
+ it "doesnt change the value" do
33
+ expect(subject["host"]).to eq(already_exist)
34
+ end
35
+ end
36
+
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 }}) }
40
+
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["host"]).to eq(producer_host)
44
+ end
45
+ end
46
+
47
+ context "when `host` key exists on the event" do
48
+ let(:already_exist) { "already_exist" }
49
+ let(:event_map) { super.merge({ "host" => already_exist }) }
50
+
51
+ it "doesn't override it" do
52
+ expect(subject["host"]).to eq(already_exist)
53
+ end
54
+ end
55
+ end
56
+ end