logstash-input-beats 3.1.0.beta1-java

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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +131 -0
  3. data/CONTRIBUTORS +17 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +14 -0
  6. data/NOTICE.TXT +5 -0
  7. data/PROTOCOL.md +127 -0
  8. data/README.md +98 -0
  9. data/VERSION +1 -0
  10. data/lib/logstash-input-beats_jars.rb +17 -0
  11. data/lib/logstash/inputs/beats.rb +184 -0
  12. data/lib/logstash/inputs/beats/codec_callback_listener.rb +26 -0
  13. data/lib/logstash/inputs/beats/decoded_event_transform.rb +34 -0
  14. data/lib/logstash/inputs/beats/event_transform_common.rb +48 -0
  15. data/lib/logstash/inputs/beats/message_listener.rb +96 -0
  16. data/lib/logstash/inputs/beats/raw_event_transform.rb +18 -0
  17. data/lib/logstash/inputs/beats/tls.rb +40 -0
  18. data/lib/tasks/build.rake +15 -0
  19. data/lib/tasks/test.rake +65 -0
  20. data/logstash-input-beats.gemspec +41 -0
  21. data/spec/inputs/beats/codec_callback_listener_spec.rb +33 -0
  22. data/spec/inputs/beats/decoded_event_transform_spec.rb +74 -0
  23. data/spec/inputs/beats/event_transform_common_spec.rb +11 -0
  24. data/spec/inputs/beats/message_listener_spec.rb +108 -0
  25. data/spec/inputs/beats/raw_event_transform_spec.rb +26 -0
  26. data/spec/inputs/beats/tls_spec.rb +39 -0
  27. data/spec/inputs/beats_spec.rb +99 -0
  28. data/spec/integration/filebeat_spec.rb +234 -0
  29. data/spec/integration/logstash_forwarder_spec.rb +104 -0
  30. data/spec/spec_helper.rb +14 -0
  31. data/spec/support/client_process_helpers.rb +28 -0
  32. data/spec/support/file_helpers.rb +61 -0
  33. data/spec/support/flores_extensions.rb +82 -0
  34. data/spec/support/integration_shared_context.rb +73 -0
  35. data/spec/support/logstash_test.rb +66 -0
  36. data/spec/support/shared_examples.rb +56 -0
  37. data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-annotations/2.7.5/jackson-annotations-2.7.5.jar +0 -0
  38. data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-core/2.7.5/jackson-core-2.7.5.jar +0 -0
  39. data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-databind/2.7.5/jackson-databind-2.7.5.jar +0 -0
  40. data/vendor/jar-dependencies/com/fasterxml/jackson/module/jackson-module-afterburner/2.7.5/jackson-module-afterburner-2.7.5.jar +0 -0
  41. data/vendor/jar-dependencies/io/netty/netty-all/4.1.1.Final/netty-all-4.1.1.Final.jar +0 -0
  42. data/vendor/jar-dependencies/io/netty/netty-tcnative-boringssl-static/1.1.33.Fork17/netty-tcnative-boringssl-static-1.1.33.Fork17.jar +0 -0
  43. data/vendor/jar-dependencies/org/apache/logging/log4j/log4j-1.2-api/2.6.1/log4j-1.2-api-2.6.1.jar +0 -0
  44. data/vendor/jar-dependencies/org/apache/logging/log4j/log4j-api/2.6.1/log4j-api-2.6.1.jar +0 -0
  45. data/vendor/jar-dependencies/org/apache/logging/log4j/log4j-core/2.6.1/log4j-core-2.6.1.jar +0 -0
  46. data/vendor/jar-dependencies/org/apache/logging/log4j/log4j-slf4j-impl/2.6.1/log4j-slf4j-impl-2.6.1.jar +0 -0
  47. data/vendor/jar-dependencies/org/bouncycastle/bcpkix-jdk15on/1.54/bcpkix-jdk15on-1.54.jar +0 -0
  48. data/vendor/jar-dependencies/org/bouncycastle/bcprov-jdk15on/1.54/bcprov-jdk15on-1.54.jar +0 -0
  49. data/vendor/jar-dependencies/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar +0 -0
  50. data/vendor/jar-dependencies/org/logstash/beats/logstash-input-beats/3.1.0.beta1/logstash-input-beats-3.1.0.beta1.jar +0 -0
  51. metadata +313 -0
@@ -0,0 +1,104 @@
1
+ # encoding: utf-8
2
+ require "logstash/inputs/beats"
3
+ require "logstash/json"
4
+ require "fileutils"
5
+ require_relative "../support/flores_extensions"
6
+ require_relative "../support/file_helpers"
7
+ require_relative "../support/integration_shared_context"
8
+ require_relative "../support/client_process_helpers"
9
+ require "spec_helper"
10
+
11
+ LSF_BINARY = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "vendor", "logstash-forwarder", "logstash-forwarder"))
12
+
13
+ describe "Logstash-Forwarder", :integration => true do
14
+ include ClientProcessHelpers
15
+
16
+ before :all do
17
+ unless File.exist?(LSF_BINARY)
18
+ raise "Cannot find `logstash-forwarder` binary in `vendor/logstash-forwarder` Did you run `bundle exec rake test:integration:setup` before running the integration suite?"
19
+ end
20
+ end
21
+
22
+ include FileHelpers
23
+ include_context "beats configuration"
24
+
25
+ let(:client_wait_time) { 5 }
26
+
27
+ # Filebeat related variables
28
+ let(:cmd) { [lsf_exec, "-config", lsf_config_path] }
29
+
30
+ let(:lsf_exec) { LSF_BINARY }
31
+ let(:registry_file) { File.expand_path(File.join(File.dirname(__FILE__), "..", "..", ".logstash-forwarder")) }
32
+ let_tmp_file(:lsf_config_path) { lsf_config }
33
+ let(:log_file) { f = Stud::Temporary.file; f.close; f.path }
34
+ let(:lsf_config) do
35
+ <<-CONFIG
36
+ {
37
+ "network": {
38
+ "servers": [ "#{host}:#{port}" ],
39
+ "ssl ca": "#{certificate_authorities}"
40
+ },
41
+ "files": [
42
+ { "paths": [ "#{log_file}" ] }
43
+ ]
44
+ }
45
+ CONFIG
46
+ end
47
+ #
48
+
49
+ before :each do
50
+ FileUtils.rm_rf(registry_file) # ensure clean state between runs
51
+ start_client
52
+ sleep(1)
53
+ # let LSF start and than write the logs
54
+ File.open(log_file, "a") do |f|
55
+ f.write(events.join("\n") + "\n")
56
+ end
57
+ sleep(1) # give some time to the clients to pick up the changes
58
+ stop_client
59
+ end
60
+
61
+ after :each do
62
+ stop_client
63
+ end
64
+
65
+ xcontext "Plain TCP" do
66
+ include ClientProcessHelpers
67
+
68
+ let(:certificate_authorities) { "" }
69
+
70
+ it "should not send any events" do
71
+ expect(queue.size).to eq(0), @execution_output
72
+ end
73
+ end
74
+
75
+ context "TLS" do
76
+ context "Server Verification" do
77
+ let(:input_config) do
78
+ super.merge({
79
+ "ssl" => true,
80
+ "ssl_certificate" => certificate_file,
81
+ "ssl_key" => certificate_key_file,
82
+ })
83
+ end
84
+
85
+ let(:certificate_data) { Flores::PKI.generate }
86
+ let_tmp_file(:certificate_file) { certificate_data.first }
87
+ let_tmp_file(:certificate_key_file) { certificate_data.last }
88
+ let(:certificate_authorities) { certificate_file }
89
+
90
+ context "self signed certificate" do
91
+ include_examples "send events"
92
+ end
93
+
94
+ context "invalid CA on the client" do
95
+ let(:invalid_data) { Flores::PKI.generate }
96
+ let(:certificate_authorities) { f = Stud::Temporary.file; f.close; f.path }
97
+
98
+ it "should not send any events" do
99
+ expect(queue.size).to eq(0), @execution_output
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+ require 'rspec'
3
+ require 'rspec/mocks'
4
+ require 'rspec/wait'
5
+ require "logstash/devutils/rspec/spec_helper"
6
+ require "logstash/codecs/plain"
7
+ require_relative "support/logstash_test"
8
+
9
+ $: << File.realpath(File.join(File.dirname(__FILE__), "..", "lib"))
10
+
11
+ RSpec.configure do |config|
12
+ config.order = :rand
13
+ end
14
+
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ require "childprocess"
3
+ module ClientProcessHelpers
4
+ def start_client(timeout = 1)
5
+ @client_out = Stud::Temporary.file
6
+ @client_out.sync
7
+
8
+ @process = ChildProcess.build(*cmd)
9
+ @process.duplex = true
10
+ @process.io.stdout = @process.io.stderr = @client_out
11
+ ChildProcess.posix_spawn = true
12
+ @process.start
13
+
14
+ sleep(0.1)
15
+ @client_out.rewind
16
+
17
+ # can be used to helper debugging when a test fails
18
+ @execution_output = @client_out.read
19
+ end
20
+
21
+ def stop_client
22
+ begin
23
+ @process.poll_for_exit(5)
24
+ rescue ChildProcess::TimeoutError
25
+ Process.kill("KILL", @process.pid)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+
3
+ # Add an helper method named `let_tmp_file` to spec example, this
4
+ # helper will create a temporary file with the content from the block,
5
+ # it behave like a normal `let` statement. Also to make things easier temporary
6
+ # debug it will create another `let` statement with the actual content of the block.
7
+ #
8
+ # Example:
9
+ # ```
10
+ # let_tmp_file(:hello_world_file) { "Hello world" } # return a path to a tmp file containing "Hello World"
11
+ # and will create this debug `let`, the value of the file will be the same.
12
+ # let(:hello_world_file_content) # return "Hello world"
13
+ #
14
+ module FileHelpers
15
+ AUTO_CLEAN = true
16
+
17
+ def self.included(base)
18
+ base.extend(ClassMethods)
19
+ end
20
+
21
+ def write_to_tmp_file(content)
22
+ file = Stud::Temporary.file
23
+ file.write(content.to_s)
24
+ file.close
25
+ file.path
26
+ end
27
+
28
+ module ClassMethods
29
+ def let_empty_tmp_file(name, &block)
30
+ let(name) do
31
+ path = nil
32
+ f = Stud::Temporary.file
33
+ f.close
34
+ path = f.path
35
+ @__let_tmp_files = [] unless @__let_tmp_files
36
+ @__let_tmp_files << path
37
+ path
38
+ end
39
+ end
40
+
41
+ def let_tmp_file(name, &block)
42
+ after :each do
43
+ if @__let_tmp_files && FileHelpers::AUTO_CLEAN
44
+ @__let_tmp_files.each do |f|
45
+ FileUtils.rm_f(f)
46
+ end
47
+ end
48
+ end
49
+
50
+ name_content = "#{name}_content"
51
+ let(name_content, &block)
52
+ let(name) do
53
+ content = __send__(name_content)
54
+ path = write_to_tmp_file(content)
55
+ @__let_tmp_files = [] unless @__let_tmp_files
56
+ @__let_tmp_files << path
57
+ path
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+ require "flores/pki"
3
+ require "flores/random"
4
+
5
+ module Flores
6
+ module Random
7
+ DEFAULT_PORT_RANGE = 1024..65535
8
+ DEFAULT_PORT_CHECK_TIMEOUT = 1
9
+ DEFAULT_MAXIMUM_PORT_FIND_TRY = 15
10
+
11
+ class << self
12
+ def port(range = DEFAULT_PORT_RANGE)
13
+ try = 0
14
+ while try < DEFAULT_MAXIMUM_PORT_FIND_TRY
15
+ candidate = integer(range)
16
+
17
+ if port_available?(candidate)
18
+ break
19
+ else
20
+ try += 1
21
+ end
22
+ end
23
+
24
+ raise "Flores.random_port: Cannot find an available port, tried #{DEFAULT_MAXIMUM_PORT_FIND_TRY} times, range was: #{range}" if try == DEFAULT_MAXIMUM_PORT_FIND_TRY
25
+
26
+ candidate
27
+ end
28
+
29
+ def port_available?(port)
30
+ begin
31
+ server = TCPServer.new(port)
32
+ available = true
33
+ rescue # Assume that any errors can do this
34
+ available = false
35
+ ensure
36
+ server.close if server
37
+ end
38
+
39
+ return available
40
+ end
41
+ end
42
+ end
43
+
44
+ module PKI
45
+ DEFAULT_CERTIFICATE_OPTIONS = {
46
+ :duration => Flores::Random.number(100..2000),
47
+ :key_size => GENERATE_DEFAULT_KEY_SIZE,
48
+ :exponent => GENERATE_DEFAULT_EXPONENT,
49
+ :want_signature_ability => false
50
+ }
51
+
52
+ def self.chain_certificates(*certificates)
53
+ certificates.join("\n")
54
+ end
55
+
56
+ def self.create_intermediate_certificate(subject, signing_certificate, signing_private_key, options = {})
57
+ create_a_signed_certificate(subject, signing_certificate, signing_private_key, options.merge({ :want_signature_ability => true }))
58
+ end
59
+
60
+ def self.create_client_certicate(subject, signing_certificate, signing_private_key, options = {})
61
+ create_a_signed_certificate(subject, signing_certificate, signing_private_key, options)
62
+ end
63
+
64
+ private
65
+ def self.create_a_signed_certificate(subject, signing_certificate, signing_private_key, options = {})
66
+ options = DEFAULT_CERTIFICATE_OPTIONS.merge(options)
67
+
68
+ client_key = OpenSSL::PKey::RSA.new(options[:key_size], options[:exponent])
69
+
70
+ csr = Flores::PKI::CertificateSigningRequest.new
71
+ csr.start_time = Time.now
72
+ csr.expire_time = csr.start_time + options[:duration]
73
+ csr.public_key = client_key.public_key
74
+ csr.subject = subject
75
+ csr.signing_key = signing_private_key
76
+ csr.signing_certificate = signing_certificate
77
+ csr.want_signature_ability = options[:want_signature_ability]
78
+
79
+ [csr.create, client_key]
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,73 @@
1
+ # encoding: utf-8
2
+ require "flores/random"
3
+
4
+ shared_examples "send events" do
5
+ it "successfully send the events" do
6
+ wait(20).for { queue.size }.to eq(number_of_events), "Expected: #{number_of_events} got: #{queue.size}, execution output:\n #{@execution_output}"
7
+ expect(queue.collect { |e| e.get("message") }).to eq(events)
8
+ end
9
+ end
10
+
11
+ shared_examples "doesn't send events" do
12
+ it "doesn't send any events" do
13
+ expect(queue.size).to eq(0), "Expected: #{number_of_events} got: #{queue.size}, execution output:\n #{@execution_output}"
14
+ end
15
+ end
16
+
17
+ shared_context "beats configuration" do
18
+ # common
19
+ let(:port) { Flores::Random.port }
20
+ let(:host) { "localhost" }
21
+
22
+ let(:queue) { [] }
23
+ let_tmp_file(:log_file) { events.join("\n") + "\n" } # make sure we end of line
24
+ let(:number_of_events) { 5 }
25
+ let(:event) { "Hello world" }
26
+ let(:events) do
27
+ events = []
28
+ number_of_events.times { |n| events << "#{event} #{n}" }
29
+ events
30
+ end
31
+
32
+ let(:input_config) do
33
+ {
34
+ "host" => host,
35
+ "port" => port
36
+ }
37
+ end
38
+
39
+ let(:beats) do
40
+ LogStash::Inputs::Beats.new(input_config)
41
+ end
42
+
43
+ before :each do
44
+ beats.register
45
+
46
+ @abort_on_exception = Thread.abort_on_exception
47
+ Thread.abort_on_exception = true
48
+
49
+ @server = Thread.new do
50
+ begin
51
+ # use to know what lumberjack is actually doing
52
+ if ENV["DEBUG"]
53
+ logger = Logger.new(STDOUT)
54
+ beats.logger = Cabin::Channel.new
55
+ beats.logger.subscribe(logger)
56
+ beats.logger.level = :debug
57
+ end
58
+
59
+ beats.run(queue)
60
+ rescue => e
61
+ retry unless beats.stop?
62
+ end
63
+ end
64
+ @server.abort_on_exception = true
65
+
66
+ sleep(1) while @server.status != "run"
67
+ end
68
+
69
+ after(:each) do
70
+ beats.stop
71
+ Thread.abort_on_exception = @abort_on_exception
72
+ end
73
+ end
@@ -0,0 +1,66 @@
1
+ require "stud/temporary"
2
+
3
+
4
+ # namespace the Dummy* classes, they are reused names
5
+ # use a more specific module name to prevent clashes
6
+ module BeatsInputTest
7
+ class Certicate
8
+ attr_reader :ssl_key, :ssl_cert
9
+
10
+ def initialize
11
+ @ssl_cert = Stud::Temporary.pathname("ssl_certificate")
12
+ @ssl_key = Stud::Temporary.pathname("ssl_key")
13
+
14
+ system("openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout #{ssl_key} -out #{ssl_cert} -subj /CN=localhost > /dev/null 2>&1")
15
+ end
16
+ end
17
+
18
+ class << self
19
+ def certificate
20
+ Certicate.new
21
+ end
22
+
23
+ def random_port
24
+ rand(2000..10000)
25
+ end
26
+ end
27
+
28
+ class DummyNeverBlockedQueue < Array
29
+ def offer(element, timeout = nil)
30
+ push(element)
31
+ end
32
+
33
+ alias_method :take, :shift
34
+ end
35
+
36
+ class DummyConnection
37
+ def initialize(events)
38
+ @events = events
39
+ end
40
+
41
+ def run
42
+ @events.each do |element|
43
+ yield element[:map], element[:identity_stream]
44
+ end
45
+ end
46
+
47
+ def peer
48
+ "localhost:5555"
49
+ end
50
+ end
51
+
52
+ class DummyCodec
53
+ def register() end
54
+ def decode(*) end
55
+ def clone() self; end
56
+ def base_codec
57
+ self
58
+ end
59
+ def self.config_name
60
+ "dummy"
61
+ end
62
+ end
63
+ end
64
+
65
+
66
+
@@ -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.get("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.get("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.get("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.get("host")).to eq(already_exist)
53
+ end
54
+ end
55
+ end
56
+ end