logstash-input-beats 3.1.0.beta1-java

Sign up to get free protection for your applications and to get access to all the features.
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