logstash-output-elasticsearch-leprechaun-fork 1.0.8

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 (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/CHANGELOG.md +30 -0
  4. data/CONTRIBUTORS +31 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE +13 -0
  7. data/NOTICE.TXT +5 -0
  8. data/README.md +98 -0
  9. data/Rakefile +1 -0
  10. data/lib/logstash-output-elasticsearch_jars.rb +5 -0
  11. data/lib/logstash/outputs/elasticsearch.rb +784 -0
  12. data/lib/logstash/outputs/elasticsearch/elasticsearch-template.json +41 -0
  13. data/lib/logstash/outputs/elasticsearch/protocol.rb +339 -0
  14. data/logstash-output-elasticsearch.gemspec +40 -0
  15. data/spec/es_spec_helper.rb +65 -0
  16. data/spec/integration/outputs/elasticsearch/node_spec.rb +36 -0
  17. data/spec/integration/outputs/index_spec.rb +90 -0
  18. data/spec/integration/outputs/retry_spec.rb +156 -0
  19. data/spec/integration/outputs/routing_spec.rb +114 -0
  20. data/spec/integration/outputs/secure_spec.rb +113 -0
  21. data/spec/integration/outputs/templates_spec.rb +97 -0
  22. data/spec/integration/outputs/transport_create_spec.rb +94 -0
  23. data/spec/integration/outputs/update_spec.rb +87 -0
  24. data/spec/unit/outputs/elasticsearch/protocol_spec.rb +54 -0
  25. data/spec/unit/outputs/elasticsearch_proxy_spec.rb +59 -0
  26. data/spec/unit/outputs/elasticsearch_spec.rb +183 -0
  27. data/spec/unit/outputs/elasticsearch_ssl_spec.rb +82 -0
  28. data/vendor/jar-dependencies/runtime-jars/antlr-runtime-3.5.jar +0 -0
  29. data/vendor/jar-dependencies/runtime-jars/asm-4.1.jar +0 -0
  30. data/vendor/jar-dependencies/runtime-jars/asm-commons-4.1.jar +0 -0
  31. data/vendor/jar-dependencies/runtime-jars/elasticsearch-1.7.0.jar +0 -0
  32. data/vendor/jar-dependencies/runtime-jars/lucene-analyzers-common-4.10.4.jar +0 -0
  33. data/vendor/jar-dependencies/runtime-jars/lucene-core-4.10.4.jar +0 -0
  34. data/vendor/jar-dependencies/runtime-jars/lucene-grouping-4.10.4.jar +0 -0
  35. data/vendor/jar-dependencies/runtime-jars/lucene-highlighter-4.10.4.jar +0 -0
  36. data/vendor/jar-dependencies/runtime-jars/lucene-join-4.10.4.jar +0 -0
  37. data/vendor/jar-dependencies/runtime-jars/lucene-memory-4.10.4.jar +0 -0
  38. data/vendor/jar-dependencies/runtime-jars/lucene-misc-4.10.4.jar +0 -0
  39. data/vendor/jar-dependencies/runtime-jars/lucene-queries-4.10.4.jar +0 -0
  40. data/vendor/jar-dependencies/runtime-jars/lucene-queryparser-4.10.4.jar +0 -0
  41. data/vendor/jar-dependencies/runtime-jars/lucene-sandbox-4.10.4.jar +0 -0
  42. data/vendor/jar-dependencies/runtime-jars/lucene-spatial-4.10.4.jar +0 -0
  43. data/vendor/jar-dependencies/runtime-jars/lucene-suggest-4.10.4.jar +0 -0
  44. data/vendor/jar-dependencies/runtime-jars/spatial4j-0.4.1.jar +0 -0
  45. metadata +246 -0
@@ -0,0 +1,36 @@
1
+ require_relative "../../../../spec/es_spec_helper"
2
+ require "logstash/outputs/elasticsearch/protocol"
3
+
4
+ describe "elasticsearch node client", :integration => true do
5
+ # Test ElasticSearch Node Client
6
+ # Reference: http://www.elasticsearch.org/guide/reference/modules/discovery/zen/
7
+
8
+ subject { LogStash::Outputs::Elasticsearch::Protocols::NodeClient }
9
+
10
+ it "should support hosts in both string and array" do
11
+ # Because we defined *hosts* method in NodeClient as private,
12
+ # we use *obj.send :method,[args...]* to call method *hosts*
13
+
14
+ # Node client should support host in string
15
+ # Case 1: default :host in string
16
+ insist { subject.send :hosts, :host => "host",:port => 9300 } == "host:9300"
17
+ # Case 2: :port =~ /^\d+_\d+$/
18
+ insist { subject.send :hosts, :host => "host",:port => "9300-9302"} == "host:9300,host:9301,host:9302"
19
+ # Case 3: :host =~ /^.+:.+$/
20
+ insist { subject.send :hosts, :host => "host:9303",:port => 9300 } == "host:9303"
21
+ # Case 4: :host =~ /^.+:.+$/ and :port =~ /^\d+_\d+$/
22
+ insist { subject.send :hosts, :host => "host:9303",:port => "9300-9302"} == "host:9303"
23
+
24
+ # Node client should support host in array
25
+ # Case 5: :host in array with single item
26
+ insist { subject.send :hosts, :host => ["host"],:port => 9300 } == ("host:9300")
27
+ # Case 6: :host in array with more than one items
28
+ insist { subject.send :hosts, :host => ["host1","host2"],:port => 9300 } == "host1:9300,host2:9300"
29
+ # Case 7: :host in array with more than one items and :port =~ /^\d+_\d+$/
30
+ insist { subject.send :hosts, :host => ["host1","host2"],:port => "9300-9302" } == "host1:9300,host1:9301,host1:9302,host2:9300,host2:9301,host2:9302"
31
+ # Case 8: :host in array with more than one items and some :host =~ /^.+:.+$/
32
+ insist { subject.send :hosts, :host => ["host1","host2:9303"],:port => 9300 } == "host1:9300,host2:9303"
33
+ # Case 9: :host in array with more than one items, :port =~ /^\d+_\d+$/ and some :host =~ /^.+:.+$/
34
+ insist { subject.send :hosts, :host => ["host1","host2:9303"],:port => "9300-9302" } == "host1:9300,host1:9301,host1:9302,host2:9303"
35
+ end
36
+ end
@@ -0,0 +1,90 @@
1
+ require_relative "../../../spec/es_spec_helper"
2
+
3
+ shared_examples "an indexer" do
4
+ let(:index) { 10.times.collect { rand(10).to_s }.join("") }
5
+ let(:type) { 10.times.collect { rand(10).to_s }.join("") }
6
+ let(:event_count) { 10000 + rand(500) }
7
+ let(:flush_size) { rand(200) + 1 }
8
+ let(:config) { "not implemented" }
9
+
10
+ it "ships events" do
11
+ insist { config } != "not implemented"
12
+
13
+ pipeline = LogStash::Pipeline.new(config)
14
+ pipeline.run
15
+
16
+ index_url = "http://#{get_host}:#{get_port('http')}/#{index}"
17
+
18
+ ftw = FTW::Agent.new
19
+ ftw.post!("#{index_url}/_refresh")
20
+
21
+ # Wait until all events are available.
22
+ Stud::try(10.times) do
23
+ data = ""
24
+ response = ftw.get!("#{index_url}/_count?q=*")
25
+ response.read_body { |chunk| data << chunk }
26
+ result = LogStash::Json.load(data)
27
+ cur_count = result["count"]
28
+ insist { cur_count } == event_count
29
+ end
30
+
31
+ response = ftw.get!("#{index_url}/_search?q=*&size=1000")
32
+ data = ""
33
+ response.read_body { |chunk| data << chunk }
34
+ result = LogStash::Json.load(data)
35
+ result["hits"]["hits"].each do |doc|
36
+ insist { doc["_type"] } == type
37
+ insist { doc["_index"] } == index
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "an indexer with custom index_type", :integration => true do
43
+ it_behaves_like "an indexer" do
44
+ let(:config) {
45
+ <<-CONFIG
46
+ input {
47
+ generator {
48
+ message => "hello world"
49
+ count => #{event_count}
50
+ type => "#{type}"
51
+ }
52
+ }
53
+ output {
54
+ elasticsearch {
55
+ host => "#{get_host()}"
56
+ port => "#{get_port('http')}"
57
+ protocol => "http"
58
+ index => "#{index}"
59
+ flush_size => #{flush_size}
60
+ }
61
+ }
62
+ CONFIG
63
+ }
64
+ end
65
+ end
66
+
67
+ describe "an indexer with no type value set (default to logs)", :integration => true do
68
+ it_behaves_like "an indexer" do
69
+ let(:type) { "logs" }
70
+ let(:config) {
71
+ <<-CONFIG
72
+ input {
73
+ generator {
74
+ message => "hello world"
75
+ count => #{event_count}
76
+ }
77
+ }
78
+ output {
79
+ elasticsearch {
80
+ host => "#{get_host()}"
81
+ port => "#{get_port('http')}"
82
+ protocol => "http"
83
+ index => "#{index}"
84
+ flush_size => #{flush_size}
85
+ }
86
+ }
87
+ CONFIG
88
+ }
89
+ end
90
+ end
@@ -0,0 +1,156 @@
1
+ require "logstash/outputs/elasticsearch"
2
+ require_relative "../../../spec/es_spec_helper"
3
+
4
+ describe "failures in bulk class expected behavior", :integration => true do
5
+ let(:template) { '{"template" : "not important, will be updated by :index"}' }
6
+ let(:event1) { LogStash::Event.new("somevalue" => 100, "@timestamp" => "2014-11-17T20:37:17.223Z", "@metadata" => {"retry_count" => 0}) }
7
+ let(:action1) { ["index", {:_id=>nil, :_routing=>nil, :_index=>"logstash-2014.11.17", :_type=>"logs"}, event1] }
8
+ let(:event2) { LogStash::Event.new("geoip" => { "location" => [ 0.0, 0.0] }, "@timestamp" => "2014-11-17T20:37:17.223Z", "@metadata" => {"retry_count" => 0}) }
9
+ let(:action2) { ["index", {:_id=>nil, :_routing=>nil, :_index=>"logstash-2014.11.17", :_type=>"logs"}, event2] }
10
+ let(:invalid_event) { LogStash::Event.new("geoip" => { "location" => "notlatlon" }, "@timestamp" => "2014-11-17T20:37:17.223Z") }
11
+ let(:max_retries) { 3 }
12
+
13
+ def mock_actions_with_response(*resp)
14
+ LogStash::Outputs::Elasticsearch::Protocols::HTTPClient
15
+ .any_instance.stub(:bulk).and_return(*resp)
16
+ LogStash::Outputs::Elasticsearch::Protocols::NodeClient
17
+ .any_instance.stub(:bulk).and_return(*resp)
18
+ end
19
+
20
+ ["transport", "http"].each do |protocol|
21
+ context "with protocol => #{protocol}" do
22
+ subject! do
23
+ settings = {
24
+ "manage_template" => true,
25
+ "index" => "logstash-2014.11.17",
26
+ "template_overwrite" => true,
27
+ "protocol" => protocol,
28
+ "host" => get_host(),
29
+ "port" => get_port(protocol),
30
+ "retry_max_items" => 10,
31
+ "retry_max_interval" => 1,
32
+ "max_retries" => max_retries
33
+ }
34
+ next LogStash::Outputs::ElasticSearch.new(settings)
35
+ end
36
+
37
+ before :each do
38
+ # Delete all templates first.
39
+ require "elasticsearch"
40
+
41
+ # Clean ES of data before we start.
42
+ @es = get_client
43
+ @es.indices.delete_template(:name => "*")
44
+ @es.indices.delete(:index => "*")
45
+ @es.indices.refresh
46
+ end
47
+
48
+ it "should return no errors if all bulk actions are successful" do
49
+ mock_actions_with_response({"errors" => false})
50
+ expect(subject).to receive(:submit).with([action1, action2]).once.and_call_original
51
+ subject.register
52
+ subject.receive(event1)
53
+ subject.receive(event2)
54
+ subject.buffer_flush(:final => true)
55
+ sleep(2)
56
+ end
57
+
58
+ it "should raise exception and be retried by stud::buffer" do
59
+ call_count = 0
60
+ expect(subject).to receive(:submit).with([action1]).exactly(3).times do
61
+ if (call_count += 1) <= 2
62
+ raise "error first two times"
63
+ else
64
+ {"errors" => false}
65
+ end
66
+ end
67
+ subject.register
68
+ subject.receive(event1)
69
+ subject.teardown
70
+ end
71
+
72
+ it "should retry actions with response status of 503" do
73
+ mock_actions_with_response({"errors" => true, "statuses" => [200, 200, 503, 503]},
74
+ {"errors" => true, "statuses" => [200, 503]},
75
+ {"errors" => false})
76
+ expect(subject).to receive(:submit).with([action1, action1, action1, action2]).ordered.once.and_call_original
77
+ expect(subject).to receive(:submit).with([action1, action2]).ordered.once.and_call_original
78
+ expect(subject).to receive(:submit).with([action2]).ordered.once.and_call_original
79
+
80
+ subject.register
81
+ subject.receive(event1)
82
+ subject.receive(event1)
83
+ subject.receive(event1)
84
+ subject.receive(event2)
85
+ subject.buffer_flush(:final => true)
86
+ sleep(3)
87
+ end
88
+
89
+ it "should retry actions with response status of 429" do
90
+ mock_actions_with_response({"errors" => true, "statuses" => [429]},
91
+ {"errors" => false})
92
+ expect(subject).to receive(:submit).with([action1]).twice.and_call_original
93
+ subject.register
94
+ subject.receive(event1)
95
+ subject.buffer_flush(:final => true)
96
+ sleep(3)
97
+ end
98
+
99
+ it "should retry an event until max_retries reached" do
100
+ mock_actions_with_response({"errors" => true, "statuses" => [429]},
101
+ {"errors" => true, "statuses" => [429]},
102
+ {"errors" => true, "statuses" => [429]},
103
+ {"errors" => true, "statuses" => [429]},
104
+ {"errors" => true, "statuses" => [429]},
105
+ {"errors" => true, "statuses" => [429]})
106
+ expect(subject).to receive(:submit).with([action1]).exactly(max_retries).times.and_call_original
107
+ subject.register
108
+ subject.receive(event1)
109
+ subject.buffer_flush(:final => true)
110
+ sleep(3)
111
+ end
112
+
113
+ it "non-retryable errors like mapping errors (400) should be dropped and not be retried (unfortunetly)" do
114
+ subject.register
115
+ subject.receive(invalid_event)
116
+ expect(subject).not_to receive(:retry_push)
117
+ subject.teardown
118
+
119
+ @es.indices.refresh
120
+ sleep(5)
121
+ Stud::try(10.times) do
122
+ r = @es.search
123
+ insist { r["hits"]["total"] } == 0
124
+ end
125
+ end
126
+
127
+ it "successful requests should not be appended to retry queue" do
128
+ subject.register
129
+ subject.receive(event1)
130
+ expect(subject).not_to receive(:retry_push)
131
+ subject.teardown
132
+
133
+ @es.indices.refresh
134
+ sleep(5)
135
+ Stud::try(10.times) do
136
+ r = @es.search
137
+ insist { r["hits"]["total"] } == 1
138
+ end
139
+ end
140
+
141
+ it "should only index proper events" do
142
+ subject.register
143
+ subject.receive(invalid_event)
144
+ subject.receive(event1)
145
+ subject.teardown
146
+
147
+ @es.indices.refresh
148
+ sleep(5)
149
+ Stud::try(10.times) do
150
+ r = @es.search
151
+ insist { r["hits"]["total"] } == 1
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,114 @@
1
+ require_relative "../../../spec/es_spec_helper"
2
+
3
+ shared_examples "a routing indexer" do
4
+ let(:index) { 10.times.collect { rand(10).to_s }.join("") }
5
+ let(:type) { 10.times.collect { rand(10).to_s }.join("") }
6
+ let(:event_count) { 10000 + rand(500) }
7
+ let(:flush_size) { rand(200) + 1 }
8
+ let(:routing) { "not_implemented" }
9
+ let(:config) { "not_implemented" }
10
+
11
+ it "ships events" do
12
+ insist { routing } != "not_implemented"
13
+ insist { config } != "not_implemented"
14
+
15
+ pipeline = LogStash::Pipeline.new(config)
16
+ pipeline.run
17
+
18
+ index_url = "http://#{get_host()}:#{get_port('http')}/#{index}"
19
+
20
+ ftw = FTW::Agent.new
21
+ ftw.post!("#{index_url}/_refresh")
22
+
23
+ # Wait until all events are available.
24
+ Stud::try(10.times) do
25
+ data = ""
26
+ response = ftw.get!("#{index_url}/_count?q=*&routing=#{routing}")
27
+ response.read_body { |chunk| data << chunk }
28
+ result = LogStash::Json.load(data)
29
+ cur_count = result["count"]
30
+ insist { cur_count } == event_count
31
+ end
32
+ end
33
+ end
34
+
35
+ describe "(http protocol) index events with static routing", :integration => true do
36
+ it_behaves_like 'a routing indexer' do
37
+ let(:routing) { "test" }
38
+ let(:config) {
39
+ <<-CONFIG
40
+ input {
41
+ generator {
42
+ message => "hello world"
43
+ count => #{event_count}
44
+ type => "#{type}"
45
+ }
46
+ }
47
+ output {
48
+ elasticsearch {
49
+ host => "#{get_host()}"
50
+ port => "#{get_port('http')}"
51
+ protocol => "http"
52
+ index => "#{index}"
53
+ flush_size => #{flush_size}
54
+ routing => "#{routing}"
55
+ }
56
+ }
57
+ CONFIG
58
+ }
59
+ end
60
+ end
61
+
62
+ describe "(http_protocol) index events with fieldref in routing value", :integration => true do
63
+ it_behaves_like 'a routing indexer' do
64
+ let(:routing) { "test" }
65
+ let(:config) {
66
+ <<-CONFIG
67
+ input {
68
+ generator {
69
+ message => "#{routing}"
70
+ count => #{event_count}
71
+ type => "#{type}"
72
+ }
73
+ }
74
+ output {
75
+ elasticsearch {
76
+ host => "#{get_host()}"
77
+ port => "#{get_port('http')}"
78
+ protocol => "http"
79
+ index => "#{index}"
80
+ flush_size => #{flush_size}
81
+ routing => "%{message}"
82
+ }
83
+ }
84
+ CONFIG
85
+ }
86
+ end
87
+ end
88
+
89
+ describe "(transport protocol) index events with fieldref in routing value", :integration => true do
90
+ it_behaves_like 'a routing indexer' do
91
+ let(:routing) { "test" }
92
+ let(:config) {
93
+ <<-CONFIG
94
+ input {
95
+ generator {
96
+ message => "#{routing}"
97
+ count => #{event_count}
98
+ type => "#{type}"
99
+ }
100
+ }
101
+ output {
102
+ elasticsearch {
103
+ host => "#{get_host()}"
104
+ port => "#{get_port('transport')}"
105
+ protocol => "transport"
106
+ index => "#{index}"
107
+ flush_size => #{flush_size}
108
+ routing => "%{message}"
109
+ }
110
+ }
111
+ CONFIG
112
+ }
113
+ end
114
+ end
@@ -0,0 +1,113 @@
1
+ require_relative "../../../spec/es_spec_helper"
2
+
3
+ describe "send messages to ElasticSearch using HTTPS", :elasticsearch_secure => true do
4
+ subject do
5
+ require "logstash/outputs/elasticsearch"
6
+ settings = {
7
+ "protocol" => "http",
8
+ "node_name" => "logstash",
9
+ "cluster" => "elasticsearch",
10
+ "host" => "node01",
11
+ "user" => "user",
12
+ "password" => "changeme",
13
+ "ssl" => true,
14
+ "cacert" => "/tmp/ca/certs/cacert.pem",
15
+ # or
16
+ #"truststore" => "/tmp/ca/truststore.jks",
17
+ #"truststore_password" => "testeteste"
18
+ }
19
+ next LogStash::Outputs::ElasticSearch.new(settings)
20
+ end
21
+
22
+ before :each do
23
+ subject.register
24
+ end
25
+
26
+ it "sends events to ES" do
27
+ expect {
28
+ subject.receive(LogStash::Event.new("message" => "sample message here"))
29
+ subject.buffer_flush(:final => true)
30
+ }.to_not raise_error
31
+ end
32
+ end
33
+
34
+ describe "connect using HTTP Authentication", :elasticsearch_secure => true do
35
+ subject do
36
+ require "logstash/outputs/elasticsearch"
37
+ settings = {
38
+ "protocol" => "http",
39
+ "cluster" => "elasticsearch",
40
+ "host" => "node01",
41
+ "user" => "user",
42
+ "password" => "changeme",
43
+ }
44
+ next LogStash::Outputs::ElasticSearch.new(settings)
45
+ end
46
+
47
+ before :each do
48
+ subject.register
49
+ end
50
+
51
+ it "sends events to ES" do
52
+ expect {
53
+ subject.receive(LogStash::Event.new("message" => "sample message here"))
54
+ subject.buffer_flush(:final => true)
55
+ }.to_not raise_error
56
+ end
57
+ end
58
+
59
+ describe "send messages to ElasticSearch using HTTPS", :elasticsearch_secure => true do
60
+ subject do
61
+ require "logstash/outputs/elasticsearch"
62
+ settings = {
63
+ "protocol" => "http",
64
+ "node_name" => "logstash",
65
+ "cluster" => "elasticsearch",
66
+ "host" => "node01",
67
+ "user" => "user",
68
+ "password" => "changeme",
69
+ "ssl" => true,
70
+ "cacert" => "/tmp/ca/certs/cacert.pem",
71
+ # or
72
+ #"truststore" => "/tmp/ca/truststore.jks",
73
+ #"truststore_password" => "testeteste"
74
+ }
75
+ next LogStash::Outputs::ElasticSearch.new(settings)
76
+ end
77
+
78
+ before :each do
79
+ subject.register
80
+ end
81
+
82
+ it "sends events to ES" do
83
+ expect {
84
+ subject.receive(LogStash::Event.new("message" => "sample message here"))
85
+ subject.buffer_flush(:final => true)
86
+ }.to_not raise_error
87
+ end
88
+ end
89
+
90
+ describe "connect using HTTP Authentication", :elasticsearch_secure => true do
91
+ subject do
92
+ require "logstash/outputs/elasticsearch"
93
+ settings = {
94
+ "protocol" => "http",
95
+ "cluster" => "elasticsearch",
96
+ "host" => "node01",
97
+ "user" => "user",
98
+ "password" => "changeme",
99
+ }
100
+ next LogStash::Outputs::ElasticSearch.new(settings)
101
+ end
102
+
103
+ before :each do
104
+ subject.register
105
+ end
106
+
107
+ it "sends events to ES" do
108
+ expect {
109
+ subject.receive(LogStash::Event.new("message" => "sample message here"))
110
+ subject.buffer_flush(:final => true)
111
+ }.to_not raise_error
112
+ end
113
+ end