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

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 (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