logstash-output-elasticsearch-test 11.16.0-x86_64-linux

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +649 -0
  3. data/CONTRIBUTORS +34 -0
  4. data/Gemfile +16 -0
  5. data/LICENSE +202 -0
  6. data/NOTICE.TXT +5 -0
  7. data/README.md +106 -0
  8. data/docs/index.asciidoc +1369 -0
  9. data/lib/logstash/outputs/elasticsearch/data_stream_support.rb +282 -0
  10. data/lib/logstash/outputs/elasticsearch/default-ilm-policy.json +14 -0
  11. data/lib/logstash/outputs/elasticsearch/http_client/manticore_adapter.rb +155 -0
  12. data/lib/logstash/outputs/elasticsearch/http_client/pool.rb +534 -0
  13. data/lib/logstash/outputs/elasticsearch/http_client.rb +497 -0
  14. data/lib/logstash/outputs/elasticsearch/http_client_builder.rb +201 -0
  15. data/lib/logstash/outputs/elasticsearch/ilm.rb +92 -0
  16. data/lib/logstash/outputs/elasticsearch/license_checker.rb +52 -0
  17. data/lib/logstash/outputs/elasticsearch/template_manager.rb +131 -0
  18. data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-6x.json +45 -0
  19. data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-7x.json +44 -0
  20. data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-8x.json +50 -0
  21. data/lib/logstash/outputs/elasticsearch.rb +699 -0
  22. data/lib/logstash/plugin_mixins/elasticsearch/api_configs.rb +237 -0
  23. data/lib/logstash/plugin_mixins/elasticsearch/common.rb +409 -0
  24. data/lib/logstash/plugin_mixins/elasticsearch/noop_license_checker.rb +9 -0
  25. data/logstash-output-elasticsearch.gemspec +40 -0
  26. data/spec/es_spec_helper.rb +225 -0
  27. data/spec/fixtures/_nodes/6x.json +81 -0
  28. data/spec/fixtures/_nodes/7x.json +92 -0
  29. data/spec/fixtures/htpasswd +2 -0
  30. data/spec/fixtures/license_check/active.json +16 -0
  31. data/spec/fixtures/license_check/inactive.json +5 -0
  32. data/spec/fixtures/nginx_reverse_proxy.conf +22 -0
  33. data/spec/fixtures/scripts/painless/scripted_update.painless +2 -0
  34. data/spec/fixtures/scripts/painless/scripted_update_nested.painless +1 -0
  35. data/spec/fixtures/scripts/painless/scripted_upsert.painless +1 -0
  36. data/spec/fixtures/template-with-policy-es6x.json +48 -0
  37. data/spec/fixtures/template-with-policy-es7x.json +45 -0
  38. data/spec/fixtures/template-with-policy-es8x.json +50 -0
  39. data/spec/fixtures/test_certs/ca.crt +29 -0
  40. data/spec/fixtures/test_certs/ca.der.sha256 +1 -0
  41. data/spec/fixtures/test_certs/ca.key +51 -0
  42. data/spec/fixtures/test_certs/renew.sh +13 -0
  43. data/spec/fixtures/test_certs/test.crt +30 -0
  44. data/spec/fixtures/test_certs/test.der.sha256 +1 -0
  45. data/spec/fixtures/test_certs/test.key +51 -0
  46. data/spec/fixtures/test_certs/test.p12 +0 -0
  47. data/spec/fixtures/test_certs/test_invalid.crt +36 -0
  48. data/spec/fixtures/test_certs/test_invalid.key +51 -0
  49. data/spec/fixtures/test_certs/test_invalid.p12 +0 -0
  50. data/spec/fixtures/test_certs/test_self_signed.crt +32 -0
  51. data/spec/fixtures/test_certs/test_self_signed.key +54 -0
  52. data/spec/fixtures/test_certs/test_self_signed.p12 +0 -0
  53. data/spec/integration/outputs/compressed_indexing_spec.rb +70 -0
  54. data/spec/integration/outputs/create_spec.rb +67 -0
  55. data/spec/integration/outputs/data_stream_spec.rb +68 -0
  56. data/spec/integration/outputs/delete_spec.rb +63 -0
  57. data/spec/integration/outputs/ilm_spec.rb +534 -0
  58. data/spec/integration/outputs/index_spec.rb +421 -0
  59. data/spec/integration/outputs/index_version_spec.rb +98 -0
  60. data/spec/integration/outputs/ingest_pipeline_spec.rb +75 -0
  61. data/spec/integration/outputs/metrics_spec.rb +66 -0
  62. data/spec/integration/outputs/no_es_on_startup_spec.rb +78 -0
  63. data/spec/integration/outputs/painless_update_spec.rb +99 -0
  64. data/spec/integration/outputs/parent_spec.rb +94 -0
  65. data/spec/integration/outputs/retry_spec.rb +182 -0
  66. data/spec/integration/outputs/routing_spec.rb +61 -0
  67. data/spec/integration/outputs/sniffer_spec.rb +94 -0
  68. data/spec/integration/outputs/templates_spec.rb +133 -0
  69. data/spec/integration/outputs/unsupported_actions_spec.rb +75 -0
  70. data/spec/integration/outputs/update_spec.rb +114 -0
  71. data/spec/spec_helper.rb +10 -0
  72. data/spec/support/elasticsearch/api/actions/delete_ilm_policy.rb +19 -0
  73. data/spec/support/elasticsearch/api/actions/get_alias.rb +18 -0
  74. data/spec/support/elasticsearch/api/actions/get_ilm_policy.rb +18 -0
  75. data/spec/support/elasticsearch/api/actions/put_alias.rb +24 -0
  76. data/spec/support/elasticsearch/api/actions/put_ilm_policy.rb +25 -0
  77. data/spec/unit/http_client_builder_spec.rb +185 -0
  78. data/spec/unit/outputs/elasticsearch/data_stream_support_spec.rb +612 -0
  79. data/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb +151 -0
  80. data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +501 -0
  81. data/spec/unit/outputs/elasticsearch/http_client_spec.rb +339 -0
  82. data/spec/unit/outputs/elasticsearch/template_manager_spec.rb +189 -0
  83. data/spec/unit/outputs/elasticsearch_proxy_spec.rb +103 -0
  84. data/spec/unit/outputs/elasticsearch_spec.rb +1573 -0
  85. data/spec/unit/outputs/elasticsearch_ssl_spec.rb +197 -0
  86. data/spec/unit/outputs/error_whitelist_spec.rb +56 -0
  87. data/spec/unit/outputs/license_check_spec.rb +57 -0
  88. metadata +423 -0
@@ -0,0 +1,78 @@
1
+ require "logstash/outputs/elasticsearch"
2
+ require_relative "../../../spec/es_spec_helper"
3
+
4
+ describe "elasticsearch is down on startup", :integration => true do
5
+ let(:event1) { LogStash::Event.new("somevalue" => 100, "@timestamp" => "2014-11-17T20:37:17.223Z", "@metadata" => {"retry_count" => 0}) }
6
+ let(:event2) { LogStash::Event.new("message" => "a") }
7
+
8
+ subject {
9
+ LogStash::Outputs::ElasticSearch.new({
10
+ "manage_template" => true,
11
+ "index" => "logstash-2014.11.17",
12
+ "template_overwrite" => true,
13
+ "hosts" => get_host_port(),
14
+ "retry_max_interval" => 64,
15
+ "retry_initial_interval" => 2,
16
+ 'ecs_compatibility' => 'disabled'
17
+ })
18
+ }
19
+
20
+ before :each do
21
+ # Delete all templates first.
22
+ allow(Stud).to receive(:stoppable_sleep)
23
+
24
+ # Clean ES of data before we start.
25
+ @es = get_client
26
+ @es.indices.delete_template(:name => "*")
27
+ @es.indices.delete(:index => "*")
28
+ @es.indices.refresh
29
+ end
30
+
31
+ after :each do
32
+ subject.close
33
+ end
34
+
35
+ it 'should ingest events when Elasticsearch recovers before documents are sent' do
36
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:get_es_version).and_raise(
37
+ ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError.new StandardError.new("TEST: before docs are sent"), 'http://test.es/'
38
+ )
39
+ subject.register
40
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:get_es_version).and_return(ESHelper.es_version)
41
+ subject.multi_receive([event1, event2])
42
+ @es.indices.refresh
43
+ r = @es.search(index: 'logstash-*')
44
+ expect(r).to have_hits(2)
45
+ end
46
+
47
+ it 'should ingest events when Elasticsearch recovers after documents are sent' do
48
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:get_es_version).and_raise(
49
+ ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError.new StandardError.new("TEST: after docs are sent"), 'http://test.es/'
50
+ )
51
+ subject.register
52
+ Thread.new do
53
+ sleep 4
54
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:get_es_version).and_return(ESHelper.es_version)
55
+ end
56
+ subject.multi_receive([event1, event2])
57
+ @es.indices.refresh
58
+ r = @es.search(index: 'logstash-*')
59
+ expect(r).to have_hits(2)
60
+ end
61
+
62
+ it 'should get cluster_uuid when Elasticsearch recovers from license check failure' do
63
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:get_license).and_raise(
64
+ ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError.new StandardError.new("TEST: docs are sent"), 'http://test.es/_license'
65
+ )
66
+ subject.register
67
+ Thread.new do
68
+ sleep 4
69
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:get_license).and_call_original
70
+ end
71
+ subject.multi_receive([event1, event2])
72
+ @es.indices.refresh
73
+ r = @es.search(index: 'logstash-*')
74
+ expect(r).to have_hits(2)
75
+ expect(subject.plugin_metadata.get(:cluster_uuid)).not_to be_empty
76
+ expect(subject.plugin_metadata.get(:cluster_uuid)).not_to eq("_na_")
77
+ end if ESHelper.es_version_satisfies?(">=7")
78
+ end
@@ -0,0 +1,99 @@
1
+ require_relative "../../../spec/es_spec_helper"
2
+
3
+ describe "Update actions using painless scripts", :integration => true, :update_tests => 'painless' do
4
+ require "logstash/outputs/elasticsearch"
5
+
6
+ def get_es_output( options={} )
7
+ settings = {
8
+ "manage_template" => true,
9
+ "index" => "logstash-update",
10
+ "template_overwrite" => true,
11
+ "hosts" => get_host_port(),
12
+ "action" => "update"
13
+ }
14
+ LogStash::Outputs::ElasticSearch.new(settings.merge!(options))
15
+ end
16
+
17
+ before :each do
18
+ @es = get_client
19
+ # Delete all templates first.
20
+ # Clean ES of data before we start.
21
+ @es.indices.delete_template(:name => "*")
22
+ # This can fail if there are no indexes, ignore failure.
23
+ @es.indices.delete(:index => "*") rescue nil
24
+ @es.index(
25
+ :index => 'logstash-update',
26
+ :type => doc_type,
27
+ :id => "123",
28
+ :body => { :message => 'Test', :counter => 1 }
29
+ )
30
+ @es.indices.refresh
31
+ end
32
+
33
+ context "scripted updates" do
34
+ context 'with an indexed script' do
35
+ it "should increment a counter with event/doc 'count' variable with indexed script" do
36
+ @es.perform_request(:put, "_scripts/indexed_update", {}, {"script" => {"source" => "ctx._source.counter += params.event.count", "lang" => "painless"}})
37
+
38
+ plugin_parameters = {
39
+ 'document_id' => "123",
40
+ 'script' => 'indexed_update',
41
+ 'script_type' => 'indexed'
42
+ }
43
+
44
+ plugin_parameters.merge!('script_lang' => '')
45
+
46
+ subject = get_es_output(plugin_parameters)
47
+ subject.register
48
+ subject.multi_receive([LogStash::Event.new("count" => 4 )])
49
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "123", :refresh => true)
50
+ expect(r["_source"]["counter"]).to eq(5)
51
+ end
52
+ end
53
+ end
54
+
55
+ context "when update with upsert" do
56
+ it "should create new documents with provided upsert" do
57
+ subject = get_es_output({ 'document_id' => "456", 'upsert' => '{"message": "upsert message"}' })
58
+ subject.register
59
+ subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
60
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
61
+ expect(r["_source"]["message"]).to eq('upsert message')
62
+ end
63
+
64
+ it "should create new documents with event/doc as upsert" do
65
+ subject = get_es_output({ 'document_id' => "456", 'doc_as_upsert' => true })
66
+ subject.register
67
+ subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
68
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
69
+ expect(r["_source"]["message"]).to eq('sample message here')
70
+ end
71
+
72
+ it "should fail on documents with event/doc as upsert at external version" do
73
+ subject = get_es_output({ 'document_id' => "456", 'doc_as_upsert' => true, 'version' => 999, "version_type" => "external" })
74
+ expect { subject.register }.to raise_error(LogStash::ConfigurationError)
75
+ end
76
+ end
77
+
78
+ context "updates with scripted upsert" do
79
+ context 'with an inline script' do
80
+ it "should create new documents with upsert content" do
81
+ subject = get_es_output({ 'document_id' => "456", 'script' => 'ctx._source.counter = params.event.counter', 'upsert' => '{"message": "upsert message"}', 'script_type' => 'inline' })
82
+ subject.register
83
+
84
+ subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
85
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
86
+ expect(r["_source"]["message"]).to eq('upsert message')
87
+ end
88
+
89
+ it "should create new documents with event/doc as script params" do
90
+ subject = get_es_output({ 'document_id' => "456", 'script' => 'ctx._source.counter = params.event.counter', 'scripted_upsert' => true, 'script_type' => 'inline' })
91
+ subject.register
92
+ subject.multi_receive([LogStash::Event.new("counter" => 1)])
93
+ @es.indices.refresh
94
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
95
+ expect(r["_source"]["counter"]).to eq(1)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,94 @@
1
+ require_relative "../../../spec/es_spec_helper"
2
+ require "logstash/outputs/elasticsearch"
3
+
4
+ describe "join type field", :integration => true do
5
+
6
+ shared_examples "a join field based parent indexer" do
7
+ let(:index) { 10.times.collect { rand(10).to_s }.join("") }
8
+
9
+ let(:type) { ESHelper.es_version_satisfies?("< 7") ? "doc" : "_doc" }
10
+
11
+ let(:event_count) { 10000 + rand(500) }
12
+ let(:parent) { "not_implemented" }
13
+ let(:config) { "not_implemented" }
14
+ let(:parent_id) { "test" }
15
+ let(:join_field) { "join_field" }
16
+ let(:parent_relation) { "parent_type" }
17
+ let(:child_relation) { "child_type" }
18
+ let(:default_headers) {
19
+ {"Content-Type" => "application/json"}
20
+ }
21
+ subject { LogStash::Outputs::ElasticSearch.new(config) }
22
+
23
+ before do
24
+ # Add mapping and a parent document
25
+ index_url = "http://#{get_host_port()}/#{index}"
26
+
27
+ properties = {
28
+ "properties" => {
29
+ join_field => {
30
+ "type" => "join",
31
+ "relations" => { parent_relation => child_relation }
32
+ }
33
+ }
34
+ }
35
+
36
+ mapping = ESHelper.es_version_satisfies?('<7') ? { "mappings" => { type => properties } }
37
+ : { "mappings" => properties}
38
+
39
+ Manticore.put("#{index_url}", {:body => mapping.to_json, :headers => default_headers}).call
40
+ pdoc = { "message" => "ohayo", join_field => parent_relation }
41
+ Manticore.put("#{index_url}/#{type}/#{parent_id}", {:body => pdoc.to_json, :headers => default_headers}).call
42
+
43
+ subject.register
44
+ subject.multi_receive(event_count.times.map { LogStash::Event.new("link_to" => parent_id, "message" => "Hello World!", join_field => child_relation) })
45
+ end
46
+
47
+
48
+ it "ships events" do
49
+ index_url = "http://#{get_host_port()}/#{index}"
50
+
51
+ Manticore.post("#{index_url}/_refresh").call
52
+
53
+ # Wait until all events are available.
54
+ Stud::try(10.times) do
55
+ query = { "query" => { "has_parent" => { "parent_type" => parent_relation, "query" => { "match_all" => { } } } } }
56
+ response = Manticore.post("#{index_url}/_count", {:body => query.to_json, :headers => default_headers})
57
+ data = response.body
58
+ result = LogStash::Json.load(data)
59
+ cur_count = result["count"]
60
+ expect(cur_count).to eq(event_count)
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "(http protocol) index events with static parent" do
66
+ it_behaves_like 'a join field based parent indexer' do
67
+ let(:config) {
68
+ {
69
+ "hosts" => get_host_port,
70
+ "index" => index,
71
+ "parent" => parent_id,
72
+ "document_type" => type,
73
+ "join_field" => join_field,
74
+ "manage_template" => false
75
+ }
76
+ }
77
+ end
78
+ end
79
+
80
+ describe "(http_protocol) index events with fieldref in parent value" do
81
+ it_behaves_like 'a join field based parent indexer' do
82
+ let(:config) {
83
+ {
84
+ "hosts" => get_host_port,
85
+ "index" => index,
86
+ "parent" => "%{link_to}",
87
+ "document_type" => type,
88
+ "join_field" => join_field,
89
+ "manage_template" => false
90
+ }
91
+ }
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,182 @@
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) do
8
+ if ESHelper.es_version_satisfies?("< 7")
9
+ ESHelper.action_for_version(["index", {:_id=>nil, routing_field_name =>nil, :_index=>"logstash-2014.11.17", :_type=> doc_type }, event1.to_hash])
10
+ else
11
+ ESHelper.action_for_version(["index", {:_id=>nil, routing_field_name =>nil, :_index=>"logstash-2014.11.17" }, event1.to_hash])
12
+ end
13
+ end
14
+ let(:event2) { LogStash::Event.new("geoip" => { "location" => [ 0.0, 0.0] }, "@timestamp" => "2014-11-17T20:37:17.223Z", "@metadata" => {"retry_count" => 0}) }
15
+ let(:action2) do
16
+ if ESHelper.es_version_satisfies?("< 7")
17
+ ESHelper.action_for_version(["index", {:_id=>nil, routing_field_name =>nil, :_index=>"logstash-2014.11.17", :_type=> doc_type }, event2.to_hash])
18
+ else
19
+ ESHelper.action_for_version(["index", {:_id=>nil, routing_field_name =>nil, :_index=>"logstash-2014.11.17" }, event2.to_hash])
20
+ end
21
+ end
22
+ let(:invalid_event) { LogStash::Event.new("geoip" => { "location" => "notlatlon" }, "@timestamp" => "2014-11-17T20:37:17.223Z") }
23
+
24
+ def mock_actions_with_response(*resp)
25
+ raise ArgumentError, "Cannot mock actions until subject is registered and has a client!" unless subject.client
26
+
27
+ expanded_responses = resp.map do |resp|
28
+ items = resp["statuses"] && resp["statuses"].map do |status|
29
+ {"create" => {"status" => status, "error" => "Error for #{status}"}}
30
+ end
31
+
32
+ {
33
+ "errors" => resp["errors"],
34
+ "items" => items
35
+ }
36
+ end
37
+
38
+ allow(subject.client).to receive(:bulk).and_return(*expanded_responses)
39
+ end
40
+
41
+ subject! do
42
+ settings = {
43
+ "manage_template" => true,
44
+ "index" => "logstash-2014.11.17",
45
+ "template_overwrite" => true,
46
+ "hosts" => get_host_port(),
47
+ "retry_max_interval" => 64,
48
+ "retry_initial_interval" => 2,
49
+ "ecs_compatibility" => "disabled", # specs are tightly tied to non-ECS defaults
50
+ }
51
+ next LogStash::Outputs::ElasticSearch.new(settings)
52
+ end
53
+
54
+ before :each do
55
+ # Delete all templates first.
56
+ require "elasticsearch"
57
+ allow(Stud).to receive(:stoppable_sleep)
58
+
59
+ # Clean ES of data before we start.
60
+ @es = get_client
61
+ @es.indices.delete_template(:name => "*")
62
+ @es.indices.delete(:index => "*")
63
+ @es.indices.refresh
64
+ end
65
+
66
+ after :each do
67
+ subject.close
68
+ end
69
+
70
+ it "should retry exactly once if all bulk actions are successful" do
71
+ expect(subject).to receive(:submit).with([action1, action2]).once.and_call_original
72
+ subject.register
73
+ mock_actions_with_response({"errors" => false})
74
+ subject.multi_receive([event1, event2])
75
+ end
76
+
77
+ it "retry exceptions within the submit body" do
78
+ call_count = 0
79
+ subject.register
80
+
81
+ expect(subject.client).to receive(:bulk).with(anything).exactly(3).times do
82
+ if (call_count += 1) <= 2
83
+ raise "error first two times"
84
+ else
85
+ {"errors" => false}
86
+ end
87
+ end
88
+
89
+ subject.multi_receive([event1])
90
+ end
91
+
92
+ it "should retry actions with response status of 503" do expect(subject).to receive(:submit).with([action1, action1, action1, action2]).ordered.once.and_call_original
93
+ expect(subject).to receive(:submit).with([action1, action2]).ordered.once.and_call_original
94
+ expect(subject).to receive(:submit).with([action2]).ordered.once.and_call_original
95
+
96
+ subject.register
97
+ mock_actions_with_response({"errors" => true, "statuses" => [200, 200, 503, 503]},
98
+ {"errors" => true, "statuses" => [200, 503]},
99
+ {"errors" => false})
100
+
101
+ subject.multi_receive([event1, event1, event1, event2])
102
+ end
103
+
104
+ retryable_codes = [429, 502, 503]
105
+
106
+ retryable_codes.each do |code|
107
+ it "should retry actions with response status of #{code}" do
108
+ subject.register
109
+
110
+ mock_actions_with_response({"errors" => true, "statuses" => [code]},
111
+ {"errors" => false})
112
+ expect(subject).to receive(:submit).with([action1]).twice.and_call_original
113
+
114
+ subject.multi_receive([event1])
115
+ end
116
+ end
117
+
118
+ it "should retry an event infinitely until a non retryable status occurs" do
119
+ expect(subject).to receive(:submit).with([action1]).exactly(6).times.and_call_original
120
+ subject.register
121
+
122
+ mock_actions_with_response({"errors" => true, "statuses" => [429]},
123
+ {"errors" => true, "statuses" => [429]},
124
+ {"errors" => true, "statuses" => [429]},
125
+ {"errors" => true, "statuses" => [429]},
126
+ {"errors" => true, "statuses" => [429]},
127
+ {"errors" => true, "statuses" => [400]})
128
+
129
+ subject.multi_receive([event1])
130
+ end
131
+
132
+ it "should sleep for an exponentially increasing amount of time on each retry, capped by the max" do
133
+ [2, 4, 8, 16, 32, 64, 64].each_with_index do |interval,i|
134
+ expect(Stud).to receive(:stoppable_sleep).with(interval).ordered
135
+ end
136
+
137
+ subject.register
138
+
139
+ mock_actions_with_response({"errors" => true, "statuses" => [429]},
140
+ {"errors" => true, "statuses" => [429]},
141
+ {"errors" => true, "statuses" => [429]},
142
+ {"errors" => true, "statuses" => [429]},
143
+ {"errors" => true, "statuses" => [429]},
144
+ {"errors" => true, "statuses" => [429]},
145
+ {"errors" => true, "statuses" => [429]},
146
+ {"errors" => true, "statuses" => [400]})
147
+
148
+ subject.multi_receive([event1])
149
+ end
150
+
151
+ it "non-retryable errors like mapping errors (400) should be dropped and not be retried (unfortunately)" do
152
+ subject.register
153
+ expect(subject).to receive(:submit).once.and_call_original
154
+ subject.multi_receive([invalid_event])
155
+ subject.close
156
+
157
+ @es.indices.refresh
158
+ r = @es.search(index: 'logstash-*')
159
+ expect(r).to have_hits(0)
160
+ end
161
+
162
+ it "successful requests should not be appended to retry queue" do
163
+ expect(subject).to receive(:submit).once.and_call_original
164
+
165
+ subject.register
166
+ subject.multi_receive([event1])
167
+ subject.close
168
+ @es.indices.refresh
169
+ r = @es.search(index: 'logstash-*')
170
+ expect(r).to have_hits(1)
171
+ end
172
+
173
+ it "should only index proper events" do
174
+ subject.register
175
+ subject.multi_receive([invalid_event, event1])
176
+ subject.close
177
+
178
+ @es.indices.refresh
179
+ r = @es.search(index: 'logstash-*')
180
+ expect(r).to have_hits(1)
181
+ end
182
+ end
@@ -0,0 +1,61 @@
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(:routing) { "not_implemented" }
8
+ let(:config) { "not_implemented" }
9
+ subject { LogStash::Outputs::ElasticSearch.new(config) }
10
+
11
+ before do
12
+ subject.register
13
+ event_count.times do
14
+ subject.multi_receive([LogStash::Event.new("message" => "test", "type" => type)])
15
+ end
16
+ end
17
+
18
+
19
+ it "ships events" do
20
+ index_url = "http://#{get_host_port()}/#{index}"
21
+
22
+ client = Manticore::Client.new
23
+ client.post("#{index_url}/_refresh").call
24
+
25
+ # Wait until all events are available.
26
+ Stud::try(10.times) do
27
+ data = ""
28
+
29
+ response = client.get("#{index_url}/_count?q=*&routing=#{routing}").call
30
+ result = LogStash::Json.load(response.body)
31
+ cur_count = result["count"]
32
+ expect(cur_count).to eq(event_count)
33
+ end
34
+ end
35
+ end
36
+
37
+ describe "(http protocol) index events with static routing", :integration => true do
38
+ it_behaves_like 'a routing indexer' do
39
+ let(:routing) { "test" }
40
+ let(:config) {
41
+ {
42
+ "hosts" => get_host_port,
43
+ "index" => index,
44
+ "routing" => routing
45
+ }
46
+ }
47
+ end
48
+ end
49
+
50
+ describe "(http_protocol) index events with fieldref in routing value", :integration => true do
51
+ it_behaves_like 'a routing indexer' do
52
+ let(:routing) { "test" }
53
+ let(:config) {
54
+ {
55
+ "hosts" => get_host_port,
56
+ "index" => index,
57
+ "routing" => "%{message}"
58
+ }
59
+ }
60
+ end
61
+ end
@@ -0,0 +1,94 @@
1
+ require_relative "../../../spec/es_spec_helper"
2
+ require "logstash/outputs/elasticsearch/http_client"
3
+ require "json"
4
+ require "socket"
5
+
6
+ describe "pool sniffer", :integration => true do
7
+ let(:logger) { Cabin::Channel.get }
8
+ let(:adapter) { LogStash::Outputs::ElasticSearch::HttpClient::ManticoreAdapter.new(logger, {}) }
9
+ let(:es_host) { get_host_port.split(":").first }
10
+ let(:es_port) { get_host_port.split(":").last }
11
+ let(:es_ip) { IPSocket.getaddress(es_host) }
12
+ let(:initial_urls) { [::LogStash::Util::SafeURI.new("http://#{get_host_port}")] }
13
+ let(:options) do
14
+ {
15
+ :resurrect_delay => 2, # Shorten the delay a bit to speed up tests
16
+ :url_normalizer => proc {|u| u},
17
+ :metric => ::LogStash::Instrument::NullMetric.new(:dummy).namespace(:alsodummy)
18
+ }
19
+ end
20
+
21
+ subject { LogStash::Outputs::ElasticSearch::HttpClient::Pool.new(logger, adapter, initial_urls, options) }
22
+
23
+ describe("Simple sniff parsing") do
24
+ before(:each) { subject.start }
25
+
26
+ context "with single node" do
27
+ it "should execute a sniff without error" do
28
+ expect { subject.check_sniff }.not_to raise_error
29
+ end
30
+
31
+ it "should return single sniff URL" do
32
+ uris = subject.check_sniff
33
+
34
+ expect(uris.size).to eq(1)
35
+ end
36
+
37
+ it "should return the correct sniff URL" do
38
+ if ESHelper.es_version_satisfies?("<7")
39
+ # We do a more thorough check on these versions because we can more reliably guess the ip
40
+ uris = subject.check_sniff
41
+
42
+ expect(uris).to include(::LogStash::Util::SafeURI.new("//#{es_ip}:#{es_port}"))
43
+ else
44
+ # ES 1.x (and ES 7.x) returned the public hostname by default. This is hard to approximate
45
+ # so for ES1.x and 7.x we don't check the *exact* hostname
46
+ skip
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ if ESHelper.es_version_satisfies?(">= 7")
53
+ describe("Complex sniff parsing ES 7x") do
54
+ before(:each) do
55
+ response_double = double("_nodes/http", body: File.read("spec/fixtures/_nodes/7x.json"))
56
+ allow(subject).to receive(:perform_request).and_return([nil, { version: "7.0" }, response_double])
57
+ subject.start
58
+ end
59
+
60
+ context "with mixed master-only, data-only, and data + master nodes" do
61
+ it "should execute a sniff without error" do
62
+ expect { subject.check_sniff }.not_to raise_error
63
+ end
64
+
65
+ it "should return the correct sniff URLs" do
66
+ # ie. with the master-only node, and with the node name correctly set.
67
+ uris = subject.check_sniff
68
+
69
+ expect(uris).to include(::LogStash::Util::SafeURI.new("//dev-masterdata:9201"), ::LogStash::Util::SafeURI.new("//dev-data:9202"))
70
+ end
71
+ end
72
+ end
73
+ end
74
+ describe("Complex sniff parsing ES") do
75
+ before(:each) do
76
+ response_double = double("_nodes/http", body: File.read("spec/fixtures/_nodes/6x.json"))
77
+ allow(subject).to receive(:perform_request).and_return([nil, { version: "6.8" }, response_double])
78
+ subject.start
79
+ end
80
+
81
+ context "with mixed master-only, data-only, and data + master nodes" do
82
+ it "should execute a sniff without error" do
83
+ expect { subject.check_sniff }.not_to raise_error
84
+ end
85
+
86
+ it "should return the correct sniff URLs" do
87
+ # ie. without the master-only node
88
+ uris = subject.check_sniff
89
+
90
+ expect(uris).to include(::LogStash::Util::SafeURI.new("//127.0.0.1:9201"), ::LogStash::Util::SafeURI.new("//127.0.0.1:9202"), ::LogStash::Util::SafeURI.new("//127.0.0.1:9203"))
91
+ end
92
+ end
93
+ end
94
+ end