logstash-input-beats 0.9.beta1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b53c62cc3ee57d609bee5c5173a65e282c842c55
4
+ data.tar.gz: a323895a76356536d22343a9ded02bfbae632975
5
+ SHA512:
6
+ metadata.gz: 8a9db4464da8b7b22fc0d878ba0446744712a95f91e1a0fdefec4d0050cbe94a7626ae748d0947061135fa9af58eb5642c7b84f1eb34ab117c6c7710b6b44073
7
+ data.tar.gz: 16cd15dc3ca834cffb3040a17759d07726e07bef4b67fd278fc18d33b5b0db42f5623f95df7373c2a5e0c35090500594ea399b661d52128aaed11efc904d99b3
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # 0.9
2
+ - Created from `logstash-input-lumberjack` version 2.0.2 https://github.com/logstash-plugins/logstash-input-lumberjack
3
+ - Use SSL off by default
data/CONTRIBUTORS ADDED
@@ -0,0 +1,16 @@
1
+ The following is a list of people who have contributed ideas, code, bug
2
+ reports, or in general have helped logstash along its way.
3
+
4
+ Contributors:
5
+ * Colin Surprenant (colinsurprenant)
6
+ * Jordan Sissel (jordansissel)
7
+ * Kurt Hurtado (kurtado)
8
+ * Nick Ethier (nickethier)
9
+ * Pier-Hugues Pellerin (ph)
10
+ * Richard Pijnenburg (electrical)
11
+ * Suyog Rao (suyograo)
12
+
13
+ Note: If you've sent us patches, bug reports, or otherwise contributed to
14
+ Logstash, and you aren't on the list above and want to be, please let us know
15
+ and we'll make sure you're here. Contributions from folks like you are what make
16
+ open source awesome.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+ gem "jls-lumberjack", :path => "~/es/ruby-lumberjack"
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2012–2015 Elasticsearch <http://www.elastic.co>
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/NOTICE.TXT ADDED
@@ -0,0 +1,5 @@
1
+ Elasticsearch
2
+ Copyright 2012-2015 Elasticsearch
3
+
4
+ This product includes software developed by The Apache Software
5
+ Foundation (http://www.apache.org/).
data/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # Logstash Plugin
2
+
3
+ This is a plugin for [Logstash](https://github.com/elastic/logstash).
4
+
5
+ It is fully free and fully open source. The license is Apache 2.0, meaning you are pretty much free to use it however you want in whatever way.
6
+
7
+ ## Documentation
8
+
9
+ Logstash provides infrastructure to automatically generate documentation for this plugin. We use the asciidoc format to write documentation so any comments in the source code will be first converted into asciidoc and then into html. All plugin documentation are placed under one [central location](http://www.elastic.co/guide/en/logstash/current/).
10
+
11
+ - For formatting code or config example, you can use the asciidoc `[source,ruby]` directive
12
+ - For more asciidoc formatting tips, see the excellent reference here https://github.com/elastic/docs#asciidoc-guide
13
+
14
+ ## Need Help?
15
+
16
+ Need help? Try #logstash on freenode IRC or the https://discuss.elastic.co/c/logstash discussion forum.
17
+
18
+ ## Developing
19
+
20
+ ### 1. Plugin Developement and Testing
21
+
22
+ #### Code
23
+ - To get started, you'll need JRuby with the Bundler gem installed.
24
+
25
+ - Create a new plugin or clone and existing from the GitHub [logstash-plugins](https://github.com/logstash-plugins) organization. We also provide [example plugins](https://github.com/logstash-plugins?query=example).
26
+
27
+ - Install dependencies
28
+ ```sh
29
+ bundle install
30
+ ```
31
+
32
+ #### Test
33
+
34
+ - Update your dependencies
35
+
36
+ ```sh
37
+ bundle install
38
+ ```
39
+
40
+ - Run tests
41
+
42
+ ```sh
43
+ bundle exec rspec
44
+ ```
45
+
46
+ ### 2. Running your unpublished Plugin in Logstash
47
+
48
+ #### 2.1 Run in a local Logstash clone
49
+
50
+ - Edit Logstash `Gemfile` and add the local plugin path, for example:
51
+ ```ruby
52
+ gem "logstash-filter-awesome", :path => "/your/local/logstash-filter-awesome"
53
+ ```
54
+ - Install plugin
55
+ ```sh
56
+ bin/plugin install --no-verify
57
+ ```
58
+ - Run Logstash with your plugin
59
+ ```sh
60
+ bin/logstash -e 'filter {awesome {}}'
61
+ ```
62
+ At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
63
+
64
+ #### 2.2 Run in an installed Logstash
65
+
66
+ You can use the same **2.1** method to run your plugin in an installed Logstash by editing its `Gemfile` and pointing the `:path` to your local plugin development directory or you can build the gem and install it using:
67
+
68
+ - Build your plugin gem
69
+ ```sh
70
+ gem build logstash-filter-awesome.gemspec
71
+ ```
72
+ - Install the plugin from the Logstash home
73
+ ```sh
74
+ bin/plugin install /your/local/plugin/logstash-filter-awesome.gem
75
+ ```
76
+ - Start Logstash and proceed to test the plugin
77
+
78
+ ## Contributing
79
+
80
+ All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin.
81
+
82
+ Programming is not a required skill. Whatever you've seen about open source and maintainers or community members saying "send patches or die" - you will not see that here.
83
+
84
+ It is more important to the community that you are able to contribute.
85
+
86
+ For more information about contributing, see the [CONTRIBUTING](https://github.com/elastic/logstash/blob/master/CONTRIBUTING.md) file.
@@ -0,0 +1,102 @@
1
+ require "thread"
2
+ require "cabin"
3
+
4
+ module LogStash
5
+ # Largely inspired by Martin's fowler circuit breaker
6
+ class CircuitBreaker
7
+ # Raised when too many errors has occured and we refuse to execute the block
8
+ class OpenBreaker < StandardError; end
9
+
10
+ # Raised when we catch an error that we count
11
+ class HalfOpenBreaker < StandardError; end
12
+
13
+ # Error threshold before opening the breaker,
14
+ # if the breaker is open it wont execute the code.
15
+ DEFAULT_ERROR_THRESHOLD = 5
16
+
17
+ # Recover time after the breaker is open to start
18
+ # executing the method again.
19
+ DEFAULT_TIME_BEFORE_RETRY = 30
20
+
21
+ # Exceptions catched by the circuit breaker,
22
+ # too much errors and the breaker will trip.
23
+ DEFAULT_EXCEPTION_RESCUED = [StandardError]
24
+
25
+ def initialize(name, options = {}, &block)
26
+ @exceptions = Array(options.fetch(:exceptions, [StandardError]))
27
+ @error_threshold = options.fetch(:error_threshold, DEFAULT_ERROR_THRESHOLD)
28
+ @time_before_retry = options.fetch(:time_before_retry, DEFAULT_TIME_BEFORE_RETRY)
29
+ @block = block
30
+ @name = name
31
+ @mutex = Mutex.new
32
+ reset
33
+ end
34
+
35
+ def execute(args = nil)
36
+ case state
37
+ when :open
38
+ logger.warn("CircuitBreaker::Open", :name => @name)
39
+ raise OpenBreaker, "for #{@name}"
40
+ when :close, :half_open
41
+ if block_given?
42
+ yield args
43
+ else
44
+ @block.call(args)
45
+ end
46
+
47
+ if state == :half_open
48
+ logger.warn("CircuitBreaker::Close", :name => @name)
49
+ reset
50
+ end
51
+ end
52
+ rescue *@exceptions => e
53
+ logger.warn("CircuitBreaker::rescuing exceptions", :name => @name, :exception => e.class)
54
+ increment_errors(e)
55
+
56
+ raise HalfOpenBreaker
57
+ end
58
+
59
+ def closed?
60
+ state == :close || state == :half_open
61
+ end
62
+
63
+ private
64
+ def logger
65
+ @logger ||= Cabin::Channel.get(LogStash)
66
+ end
67
+
68
+ def reset
69
+ @mutex.synchronize do
70
+ @errors_count = 0
71
+ @last_failure_time = nil
72
+ end
73
+ end
74
+
75
+ def increment_errors(exception)
76
+ @mutex.synchronize do
77
+ @errors_count += 1
78
+ @last_failure_time = Time.now
79
+
80
+ logger.debug("CircuitBreaker increment errors",
81
+ :errors_count => @errors_count,
82
+ :error_threshold => @error_threshold,
83
+ :exception => exception.class,
84
+ :message => exception.message) if logger.debug?
85
+ end
86
+ end
87
+
88
+ def state
89
+ @mutex.synchronize do
90
+ if @errors_count >= @error_threshold
91
+ if Time.now - @last_failure_time > @time_before_retry
92
+ :half_open
93
+ else
94
+ :open
95
+ end
96
+ else
97
+ :close
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ require "logstash/version"
3
+ require "gems"
4
+
5
+ # This module allow this plugin to work with the v1 API.
6
+ module LogStash::CompatibilityLayerApiV1
7
+ LOGSTASH_CORE_VERSION = Gem::Version.new(LOGSTASH_VERSION)
8
+ V2_VERSION = Gem::Version.new("2.0.0.beta2")
9
+
10
+ def self.included(base)
11
+ base.send(:include, InstanceMethods) if self.is_v1?
12
+ end
13
+
14
+ def self.is_v1?
15
+ LOGSTASH_CORE_VERSION < V2_VERSION
16
+ end
17
+
18
+ # This allow this plugin to work both in V1 and v2 of logstash-core
19
+ module InstanceMethods
20
+ def stop?
21
+ false
22
+ end
23
+
24
+ def teardown
25
+ stop
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,179 @@
1
+ # encoding: utf-8
2
+ require "logstash/inputs/base"
3
+ require "logstash/namespace"
4
+ require "logstash/compatibility_layer_api_v1"
5
+ require "lumberjack"
6
+
7
+ # use Logstash provided json decoder
8
+ Lumberjack::json = LogStash::Json
9
+
10
+ # Allow Logstash to receive events from Beats
11
+ #
12
+ # https://github.com/elastic/filebeat[filebeat]
13
+ #
14
+ class LogStash::Inputs::Beats < LogStash::Inputs::Base
15
+ include LogStash::CompatibilityLayerApiV1
16
+
17
+ config_name "beats"
18
+
19
+ default :codec, "plain"
20
+
21
+ # The IP address to listen on.
22
+ config :host, :validate => :string, :default => "0.0.0.0"
23
+
24
+ # The port to listen on.
25
+ config :port, :validate => :number, :required => true
26
+
27
+ # Events are by default send in plain text, you can
28
+ # enable encryption by using `ssl` to true and configuring
29
+ # the `ssl_certificate` and `ssl_key` options.
30
+ config :ssl, :validate => :boolean, :default => false
31
+
32
+ # SSL certificate to use.
33
+ config :ssl_certificate, :validate => :path
34
+
35
+ # SSL key to use.
36
+ config :ssl_key, :validate => :path
37
+
38
+ # SSL key passphrase to use.
39
+ config :ssl_key_passphrase, :validate => :password
40
+
41
+ # The number of seconds before we raise a timeout,
42
+ # this option is useful to control how much time to wait if something is blocking the pipeline.
43
+ config :congestion_threshold, :validate => :number, :default => 5
44
+
45
+ # This is the default field that the specified codec will be applied
46
+ config :target_field_for_codec, :validate => :string, :default => "message"
47
+
48
+ # TODO(sissel): Add CA to authenticate clients with.
49
+ BUFFERED_QUEUE_SIZE = 1
50
+ RECONNECT_BACKOFF_SLEEP = 0.5
51
+
52
+ def register
53
+ require "lumberjack/server"
54
+ require "concurrent"
55
+ require "logstash/circuit_breaker"
56
+ require "logstash/sized_queue_timeout"
57
+
58
+ if !@ssl
59
+ @logger.warn("Beats: SSL Certificate will not be used") unless @ssl_certificate.nil?
60
+ @logger.warn("Beats: SSL Key will not be used") unless @ssl_key.nil?
61
+ elsif !ssl_configured?
62
+ raise LogStash::ConfigurationError, "Certificate or Certificate Key not configured"
63
+ end
64
+
65
+ @logger.info("Starting Beats input listener", :address => "#{@host}:#{@port}")
66
+ @lumberjack = Lumberjack::Server.new(:address => @host, :port => @port,
67
+ :ssl => @ssl, :ssl_certificate => @ssl_certificate, :ssl_key => @ssl_key,
68
+ :ssl_key_passphrase => @ssl_key_passphrase)
69
+
70
+ # Create a reusable threadpool, we do not limit the number of connections
71
+ # to the input, the circuit breaker with the timeout should take care
72
+ # of `blocked` threads and prevent logstash to go oom.
73
+ @threadpool = Concurrent::CachedThreadPool.new(:idletime => 15)
74
+
75
+ # in 1.5 the main SizeQueue doesnt have the concept of timeout
76
+ # We are using a small plugin buffer to move events to the internal queue
77
+ @buffered_queue = LogStash::SizedQueueTimeout.new(BUFFERED_QUEUE_SIZE)
78
+
79
+ @circuit_breaker = LogStash::CircuitBreaker.new("Beats input",
80
+ :exceptions => [LogStash::SizedQueueTimeout::TimeoutError])
81
+
82
+ end # def register
83
+
84
+ def ssl_configured?
85
+ !(@ssl_certificate.nil? || @ssl_key.nil?)
86
+ end
87
+
88
+ def target_codec_on_field?
89
+ !@target_codec_on_field.empty?
90
+ end
91
+
92
+ def run(output_queue)
93
+ start_buffer_broker(output_queue)
94
+
95
+ while !stop? do
96
+ # Wrapping the accept call into a CircuitBreaker
97
+ if @circuit_breaker.closed?
98
+ connection = @lumberjack.accept # call that creates a new connection
99
+ next if connection.nil? # if the connection is nil the connection was close.
100
+
101
+ invoke(connection, @codec.clone) do |event|
102
+ if stop?
103
+ connection.close
104
+ break
105
+ end
106
+
107
+ begin
108
+ @circuit_breaker.execute {
109
+ @buffered_queue.push(event, @congestion_threshold)
110
+ }
111
+ rescue => e
112
+ raise e
113
+ end
114
+ end
115
+ else
116
+ @logger.warn("Beats input: the pipeline is blocked, temporary refusing new connection.")
117
+ sleep(RECONNECT_BACKOFF_SLEEP)
118
+ end
119
+ end
120
+ end # def run
121
+
122
+ public
123
+ def stop
124
+ @lumberjack.close
125
+ end
126
+
127
+ private
128
+ def create_event(codec, map)
129
+ # Filebeats uses the `message` key and LSF `line`
130
+ target_field = map.delete(target_field_for_codec)
131
+
132
+ if target_field.nil?
133
+ return LogStash::Event.new(map)
134
+ else
135
+ # All codes expects to work on string
136
+ @codec.decode(target_field.to_s) do |decoded|
137
+ decorate(decoded)
138
+ map.each { |k, v| decoded[k] = v }
139
+ return decoded
140
+ end
141
+ end
142
+ end
143
+
144
+ private
145
+ def invoke(connection, codec, &block)
146
+ @threadpool.post do
147
+ begin
148
+ # If any errors occur in from the events the connection should be closed in the
149
+ # library ensure block and the exception will be handled here
150
+ connection.run { |map| block.call(create_event(codec, map)) }
151
+
152
+ # When too many errors happen inside the circuit breaker it will throw
153
+ # this exception and start refusing connection. The bubbling of theses
154
+ # exceptions make sure that the lumberjack library will close the current
155
+ # connection which will force the client to reconnect and restransmit
156
+ # his payload.
157
+ rescue LogStash::CircuitBreaker::OpenBreaker,
158
+ LogStash::CircuitBreaker::HalfOpenBreaker => e
159
+ logger.warn("Beats input: The circuit breaker has detected a slowdown or stall in the pipeline, the input is closing the current connection and rejecting new connection until the pipeline recover.", :exception => e.class)
160
+ rescue => e # If we have a malformed packet we should handle that so the input doesn't crash completely.
161
+ @logger.error("Beats input: unhandled exception", :exception => e, :backtrace => e.backtrace)
162
+ end
163
+ end
164
+ end
165
+
166
+ # The default Logstash Sizequeue doesn't support timeouts.
167
+ # This is problematic because this will make the client timeouts
168
+ # and reconnect creating multiples threads and OOMing the jvm.
169
+ #
170
+ # We are using a proxy queue supporting blocking with a timeout and
171
+ # this thread take the element from one queue into another one.
172
+ def start_buffer_broker(output_queue)
173
+ @threadpool.post do
174
+ while !stop?
175
+ output_queue << @buffered_queue.pop_no_timeout
176
+ end
177
+ end
178
+ end
179
+ end # class LogStash::Inputs::Beats
@@ -0,0 +1,64 @@
1
+ require "thread"
2
+ require "concurrent"
3
+
4
+ module LogStash
5
+ # Minimal subset implement of a SizedQueue supporting
6
+ # a timeout option on the lock.
7
+ #
8
+ # This will be part of the main Logstash's sized queue
9
+ class SizedQueueTimeout
10
+ class TimeoutError < StandardError; end
11
+
12
+ DEFAULT_TIMEOUT = 5 # in seconds
13
+
14
+ def initialize(max_size, options = {})
15
+ # `concurrent-ruby` are deprecating the `Condition`
16
+ # in favor of a Synchonization class that you need to implement.
17
+ # this was bit overkill to only check if the wait did a timeout.
18
+ @condition_in = ConditionVariable.new
19
+ @condition_out = ConditionVariable.new
20
+
21
+ @max_size = max_size
22
+ @queue = []
23
+ @mutex = Mutex.new
24
+ end
25
+
26
+ def push(obj, timeout = DEFAULT_TIMEOUT)
27
+ @mutex.synchronize do
28
+ while full? # wake up check
29
+ start_time = Concurrent.monotonic_time
30
+ @condition_out.wait(@mutex, timeout)
31
+ if start_time + timeout - Concurrent.monotonic_time < 0
32
+ raise TimeoutError
33
+ end
34
+ end
35
+
36
+ @queue << obj
37
+ @condition_in.signal
38
+
39
+ return obj
40
+ end
41
+ end
42
+ alias_method :<<, :push
43
+
44
+ def size
45
+ @mutex.synchronize { @queue.size }
46
+ end
47
+
48
+ def pop_no_timeout
49
+ @mutex.synchronize do
50
+ @condition_in.wait(@mutex) while @queue.empty? # Wake up check
51
+
52
+ obj = @queue.shift
53
+ @condition_out.signal
54
+
55
+ return obj
56
+ end
57
+ end
58
+
59
+ private
60
+ def full?
61
+ @queue.size == @max_size
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,33 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "logstash-input-beats"
3
+ s.version = "0.9.beta1"
4
+ s.licenses = ["Apache License (2.0)"]
5
+ s.summary = "Receive events using the lumberjack protocol."
6
+ 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"
7
+ s.authors = ["Elastic"]
8
+ s.email = "info@elastic.co"
9
+ s.homepage = "http://www.elastic.co/guide/en/logstash/current/index.html"
10
+ s.require_paths = ["lib"]
11
+
12
+ # Files
13
+ s.files = Dir["lib/**/*","spec/**/*","vendor/**/*","*.gemspec","*.md","CONTRIBUTORS","Gemfile","LICENSE","NOTICE.TXT"]
14
+
15
+ # Tests
16
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
17
+
18
+ # Special flag to let us know this is actually a logstash plugin
19
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "input" }
20
+
21
+ # Gem dependencies
22
+ s.add_runtime_dependency "logstash-core", ">= 1.5.4", "< 3.0.0"
23
+
24
+ s.add_runtime_dependency "logstash-codec-plain"
25
+ s.add_runtime_dependency "jls-lumberjack", [">=0.0.25.beta1"]
26
+ s.add_runtime_dependency "concurrent-ruby"
27
+
28
+ s.add_development_dependency "logstash-devutils"
29
+ s.add_development_dependency "logstash-codec-multiline"
30
+ s.add_development_dependency "flores"
31
+ s.add_development_dependency "stud"
32
+ end
33
+
@@ -0,0 +1,104 @@
1
+ # encoding: utf-8
2
+ require_relative "../spec_helper"
3
+ require "stud/temporary"
4
+ require "logstash/inputs/beats"
5
+ require "logstash/compatibility_layer_api_v1"
6
+ require "logstash/codecs/plain"
7
+ require "logstash/codecs/multiline"
8
+ require "logstash/event"
9
+ require "lumberjack/client"
10
+
11
+ describe LogStash::Inputs::Beats do
12
+ let(:connection) { double("connection") }
13
+ let(:certificate) { LogStashTest.certificate }
14
+ let(:port) { LogStashTest.random_port }
15
+ let(:queue) { Queue.new }
16
+ let(:config) { { "port" => 0, "ssl_certificate" => certificate.ssl_cert, "ssl_key" => certificate.ssl_key, "type" => "example", "tags" => "beats"} }
17
+
18
+ context "#register" do
19
+ it "raise no exception" do
20
+ plugin = LogStash::Inputs::Beats.new(config)
21
+ expect { plugin.register }.not_to raise_error
22
+ end
23
+
24
+ context "with ssl enabled" do
25
+ context "without certificate configuration" do
26
+ let(:config) {{ "port" => 0, "ssl" => true, "ssl_key" => certificate.ssl_key, "type" => "example", "tags" => "beats" }}
27
+
28
+ it "should fail to register the plugin with ConfigurationError" do
29
+ plugin = LogStash::Inputs::Beats.new(config)
30
+ expect {plugin.register}.to raise_error(LogStash::ConfigurationError)
31
+ end
32
+ end
33
+
34
+ context "without key configuration" do
35
+ let(:config) { { "port" => 0, "ssl" => true, "ssl_certificate" => certificate.ssl_cert, "type" => "example", "tags" => "Beats"} }
36
+ it "should fail to register the plugin with ConfigurationError" do
37
+ plugin = LogStash::Inputs::Beats.new(config)
38
+ expect {plugin.register}.to raise_error(LogStash::ConfigurationError)
39
+ end
40
+ end
41
+ end
42
+
43
+ context "with ssl disabled" do
44
+ context "and certificate configuration" do
45
+ let(:config) { { "port" => 0, "ssl" => false, "ssl_certificate" => certificate.ssl_cert, "type" => "example", "tags" => "Beats" } }
46
+
47
+ it "should not fail" do
48
+ plugin = LogStash::Inputs::Beats.new(config)
49
+ expect {plugin.register}.not_to raise_error
50
+ end
51
+ end
52
+
53
+ context "and certificate key configuration" do
54
+ let(:config) {{ "port" => 0, "ssl" => false, "ssl_key" => certificate.ssl_key, "type" => "example", "tags" => "beats" }}
55
+
56
+ it "should not fail" do
57
+ plugin = LogStash::Inputs::Beats.new(config)
58
+ expect {plugin.register}.not_to raise_error
59
+ end
60
+ end
61
+
62
+ context "and no certificate or key configured" do
63
+ let(:config) {{ "ssl" => false, "port" => 0, "type" => "example", "tags" => "beats" }}
64
+
65
+ it "should work just fine" do
66
+ plugin = LogStash::Inputs::Beats.new(config)
67
+ expect {plugin.register}.not_to raise_error
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "#processing of events" do
74
+ subject(:beats) { LogStash::Inputs::Beats.new(config) }
75
+
76
+ let(:lines) { {"line" => "one\ntwo\n two.2\nthree\n", "tags" => ["syslog"]} }
77
+
78
+ before do
79
+ allow(connection).to receive(:run).and_yield(lines)
80
+ beats.register
81
+ expect_any_instance_of(Lumberjack::Server).to receive(:accept).and_return(connection)
82
+ end
83
+
84
+ context "#codecs" do
85
+ let(:config) do
86
+ { "port" => port, "ssl_certificate" => certificate.ssl_cert, "ssl_key" => certificate.ssl_key,
87
+ "type" => "example", "codec" => codec }
88
+ end
89
+
90
+ let(:codec) { LogStash::Codecs::Multiline.new("pattern" => '\n', "what" => "previous") }
91
+ it "clone the codec per connection" do
92
+ expect(beats.codec).to receive(:clone).once
93
+ expect(beats).to receive(:invoke) { break }
94
+ beats.run(queue)
95
+ end
96
+ end
97
+ end
98
+
99
+ unless LogStash::CompatibilityLayerApiV1.is_v1?
100
+ context "when interrupting the plugin" do
101
+ it_behaves_like "an interruptible input plugin"
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,82 @@
1
+ require_relative "../spec_helper"
2
+ require "logstash/circuit_breaker"
3
+
4
+ class DummyErrorTest < StandardError; end
5
+
6
+ describe LogStash::CircuitBreaker do
7
+ let(:error_threshold) { 1 }
8
+ let(:options) do
9
+ {
10
+ :exceptions => [DummyErrorTest],
11
+ :error_threshold => error_threshold
12
+ }
13
+ end
14
+
15
+ subject { LogStash::CircuitBreaker.new("testing", options) }
16
+
17
+ context "when the breaker is closed" do
18
+ it "closed by default" do
19
+ expect(subject.closed?).to eq(true)
20
+ end
21
+
22
+ it "always raise an exception if an errors occur" do
23
+ expect {
24
+ subject.execute do
25
+ raise DummyErrorTest
26
+ end
27
+ }.to raise_error(LogStash::CircuitBreaker::HalfOpenBreaker)
28
+ end
29
+
30
+ it "open if we pass the errors threadshold" do
31
+ expect {
32
+ subject.execute do
33
+ raise DummyErrorTest
34
+ end
35
+ }.to raise_error(LogStash::CircuitBreaker::HalfOpenBreaker)
36
+
37
+ expect {
38
+ subject.execute do
39
+ raise DummyErrorTest
40
+ end
41
+ }.to raise_error(LogStash::CircuitBreaker::OpenBreaker)
42
+ end
43
+ end
44
+
45
+ context "When the breaker is open" do
46
+ let(:future_time) { Time.now + 3600 }
47
+
48
+ before do
49
+ # trip the breaker
50
+ (error_threshold + 1).times do
51
+ begin
52
+ subject.execute do
53
+ raise DummyErrorTest
54
+ end
55
+ rescue
56
+ end
57
+ end
58
+ end
59
+
60
+ it "#closed? should return false" do
61
+ expect(subject.closed?).to eq(false)
62
+ end
63
+
64
+ it "resets the breaker after the time before retry" do
65
+ expect(Time).to receive(:now).at_least(2).and_return(future_time)
66
+ expect(subject.closed?).to eq(true)
67
+ end
68
+
69
+ it "doesnt run the command" do
70
+ runned = false
71
+
72
+ begin
73
+ subject.execute do
74
+ runned = true
75
+ end
76
+ rescue LogStash::CircuitBreaker::OpenBreaker
77
+ end
78
+
79
+ expect(runned).to eq(false)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,100 @@
1
+ require_relative "../spec_helper"
2
+ require "logstash/sized_queue_timeout"
3
+ require "flores/random"
4
+ require "stud/try"
5
+
6
+ describe "LogStash::SizedQueueTimeout" do
7
+ let(:max_size) { Flores::Random.integer(2..100) }
8
+ let(:element) { Flores::Random.text(0..100) }
9
+
10
+ subject { LogStash::SizedQueueTimeout.new(max_size) }
11
+
12
+ it "adds element to the queue" do
13
+ subject << element
14
+ expect(subject.size).to eq(1)
15
+ end
16
+
17
+ it "allow to pop element from the queue" do
18
+ subject << element
19
+ subject << "awesome"
20
+
21
+ expect(subject.pop_no_timeout).to eq(element)
22
+ end
23
+
24
+ context "when the queue is full" do
25
+ before do
26
+ max_size.times { subject << element }
27
+ end
28
+
29
+ it "block with a timeout" do
30
+ expect {
31
+ subject << element
32
+ }.to raise_error(LogStash::SizedQueueTimeout::TimeoutError)
33
+ end
34
+
35
+ it "unblock when we pop" do
36
+ blocked = Thread.new do
37
+ subject << element
38
+ end
39
+ sleep(0.1) until blocked.stop?
40
+
41
+ expect(blocked.status).to eq("sleep")
42
+
43
+ th = Thread.new do
44
+ subject.pop_no_timeout
45
+ end
46
+ sleep(0.1) until th.stop?
47
+
48
+ expect(blocked.status).to eq(false)
49
+
50
+ blocked.join
51
+ th.join
52
+ end
53
+ end
54
+
55
+ context "when the queue is empty" do
56
+ it "block on pop" do
57
+ blocked = Thread.new do
58
+ subject.pop_no_timeout
59
+ end
60
+ sleep(0.1) until blocked.stop?
61
+
62
+ expect(blocked.status).to eq("sleep")
63
+
64
+ th = Thread.new do
65
+ subject << element
66
+ end
67
+ sleep(0.1) until th.stop?
68
+
69
+ expect(blocked.status).to eq(false)
70
+ th.join
71
+ blocked.join
72
+ end
73
+ end
74
+
75
+ context "when the queue is occupied but not full" do
76
+ before :each do
77
+ Flores::Random.iterations(1..max_size-1) { subject << "hurray" }
78
+ end
79
+
80
+ it "doesnt block on pop" do
81
+ th = Thread.new do
82
+ subject.pop_no_timeout
83
+ end
84
+ sleep(0.1) until th.stop?
85
+
86
+ expect(th.status).to eq(false)
87
+ th.join
88
+ end
89
+
90
+ it "doesnt block on push" do
91
+ th = Thread.new do
92
+ subject << element
93
+ end
94
+ sleep(0.1) until th.stop?
95
+
96
+ expect(th.status).to eq(false)
97
+ th.join
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,3 @@
1
+ require "logstash/devutils/rspec/spec_helper"
2
+ require "logstash/codecs/plain"
3
+ require_relative "support/logstash_test"
@@ -0,0 +1,23 @@
1
+ require "stud/temporary"
2
+ module LogStashTest
3
+ class Certicate
4
+ attr_reader :ssl_key, :ssl_cert
5
+
6
+ def initialize
7
+ @ssl_cert = Stud::Temporary.pathname("ssl_certificate")
8
+ @ssl_key = Stud::Temporary.pathname("ssl_key")
9
+
10
+ system("openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout #{ssl_key} -out #{ssl_cert} -subj /CN=localhost > /dev/null 2>&1")
11
+ end
12
+ end
13
+
14
+ class << self
15
+ def certificate
16
+ Certicate.new
17
+ end
18
+
19
+ def random_port
20
+ rand(2000..10000)
21
+ end
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,184 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-input-beats
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.beta1
5
+ platform: ruby
6
+ authors:
7
+ - Elastic
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - '>='
17
+ - !ruby/object:Gem::Version
18
+ version: 1.5.4
19
+ - - <
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.0
22
+ name: logstash-core
23
+ prerelease: false
24
+ type: :runtime
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.5.4
30
+ - - <
31
+ - !ruby/object:Gem::Version
32
+ version: 3.0.0
33
+ - !ruby/object:Gem::Dependency
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ name: logstash-codec-plain
40
+ prerelease: false
41
+ type: :runtime
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - '>='
51
+ - !ruby/object:Gem::Version
52
+ version: 0.0.25.beta1
53
+ name: jls-lumberjack
54
+ prerelease: false
55
+ type: :runtime
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - '>='
59
+ - !ruby/object:Gem::Version
60
+ version: 0.0.25.beta1
61
+ - !ruby/object:Gem::Dependency
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ name: concurrent-ruby
68
+ prerelease: false
69
+ type: :runtime
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ name: logstash-devutils
82
+ prerelease: false
83
+ type: :development
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ name: logstash-codec-multiline
96
+ prerelease: false
97
+ type: :development
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ name: flores
110
+ prerelease: false
111
+ type: :development
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ - !ruby/object:Gem::Dependency
118
+ requirement: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ name: stud
124
+ prerelease: false
125
+ type: :development
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ 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
132
+ email: info@elastic.co
133
+ executables: []
134
+ extensions: []
135
+ extra_rdoc_files: []
136
+ files:
137
+ - lib/logstash/circuit_breaker.rb
138
+ - lib/logstash/compatibility_layer_api_v1.rb
139
+ - lib/logstash/sized_queue_timeout.rb
140
+ - lib/logstash/inputs/beats.rb
141
+ - spec/spec_helper.rb
142
+ - spec/inputs/beats_spec.rb
143
+ - spec/logstash/circuit_breaker_spec.rb
144
+ - spec/logstash/size_queue_timeout_spec.rb
145
+ - spec/support/logstash_test.rb
146
+ - logstash-input-beats.gemspec
147
+ - CHANGELOG.md
148
+ - README.md
149
+ - CONTRIBUTORS
150
+ - Gemfile
151
+ - LICENSE
152
+ - NOTICE.TXT
153
+ homepage: http://www.elastic.co/guide/en/logstash/current/index.html
154
+ licenses:
155
+ - Apache License (2.0)
156
+ metadata:
157
+ logstash_plugin: 'true'
158
+ logstash_group: input
159
+ post_install_message:
160
+ rdoc_options: []
161
+ require_paths:
162
+ - lib
163
+ required_ruby_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - '>='
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - '>'
171
+ - !ruby/object:Gem::Version
172
+ version: 1.3.1
173
+ requirements: []
174
+ rubyforge_project:
175
+ rubygems_version: 2.1.9
176
+ signing_key:
177
+ specification_version: 4
178
+ summary: Receive events using the lumberjack protocol.
179
+ test_files:
180
+ - spec/spec_helper.rb
181
+ - spec/inputs/beats_spec.rb
182
+ - spec/logstash/circuit_breaker_spec.rb
183
+ - spec/logstash/size_queue_timeout_spec.rb
184
+ - spec/support/logstash_test.rb