logstash-output-elasticsearch-leprechaun-fork 1.0.8

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