logstash-output-elasticsearch-test 10.3.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 (75) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +397 -0
  3. data/CONTRIBUTORS +33 -0
  4. data/Gemfile +15 -0
  5. data/LICENSE +13 -0
  6. data/NOTICE.TXT +5 -0
  7. data/README.md +106 -0
  8. data/docs/index.asciidoc +899 -0
  9. data/lib/logstash/outputs/elasticsearch/common.rb +441 -0
  10. data/lib/logstash/outputs/elasticsearch/common_configs.rb +167 -0
  11. data/lib/logstash/outputs/elasticsearch/default-ilm-policy.json +14 -0
  12. data/lib/logstash/outputs/elasticsearch/elasticsearch-template-es2x.json +95 -0
  13. data/lib/logstash/outputs/elasticsearch/elasticsearch-template-es5x.json +46 -0
  14. data/lib/logstash/outputs/elasticsearch/elasticsearch-template-es6x.json +45 -0
  15. data/lib/logstash/outputs/elasticsearch/elasticsearch-template-es7x.json +44 -0
  16. data/lib/logstash/outputs/elasticsearch/elasticsearch-template-es8x.json +44 -0
  17. data/lib/logstash/outputs/elasticsearch/http_client/manticore_adapter.rb +131 -0
  18. data/lib/logstash/outputs/elasticsearch/http_client/pool.rb +495 -0
  19. data/lib/logstash/outputs/elasticsearch/http_client.rb +432 -0
  20. data/lib/logstash/outputs/elasticsearch/http_client_builder.rb +159 -0
  21. data/lib/logstash/outputs/elasticsearch/ilm.rb +113 -0
  22. data/lib/logstash/outputs/elasticsearch/template_manager.rb +61 -0
  23. data/lib/logstash/outputs/elasticsearch.rb +263 -0
  24. data/logstash-output-elasticsearch.gemspec +33 -0
  25. data/spec/es_spec_helper.rb +189 -0
  26. data/spec/fixtures/_nodes/2x_1x.json +27 -0
  27. data/spec/fixtures/_nodes/5x_6x.json +81 -0
  28. data/spec/fixtures/_nodes/7x.json +92 -0
  29. data/spec/fixtures/htpasswd +2 -0
  30. data/spec/fixtures/nginx_reverse_proxy.conf +22 -0
  31. data/spec/fixtures/scripts/groovy/scripted_update.groovy +2 -0
  32. data/spec/fixtures/scripts/groovy/scripted_update_nested.groovy +2 -0
  33. data/spec/fixtures/scripts/groovy/scripted_upsert.groovy +2 -0
  34. data/spec/fixtures/scripts/painless/scripted_update.painless +2 -0
  35. data/spec/fixtures/scripts/painless/scripted_update_nested.painless +1 -0
  36. data/spec/fixtures/scripts/painless/scripted_upsert.painless +1 -0
  37. data/spec/fixtures/template-with-policy-es6x.json +48 -0
  38. data/spec/fixtures/template-with-policy-es7x.json +45 -0
  39. data/spec/fixtures/test_certs/ca/ca.crt +32 -0
  40. data/spec/fixtures/test_certs/ca/ca.key +51 -0
  41. data/spec/fixtures/test_certs/test.crt +36 -0
  42. data/spec/fixtures/test_certs/test.key +51 -0
  43. data/spec/integration/outputs/compressed_indexing_spec.rb +69 -0
  44. data/spec/integration/outputs/create_spec.rb +67 -0
  45. data/spec/integration/outputs/delete_spec.rb +65 -0
  46. data/spec/integration/outputs/groovy_update_spec.rb +150 -0
  47. data/spec/integration/outputs/ilm_spec.rb +531 -0
  48. data/spec/integration/outputs/index_spec.rb +178 -0
  49. data/spec/integration/outputs/index_version_spec.rb +102 -0
  50. data/spec/integration/outputs/ingest_pipeline_spec.rb +74 -0
  51. data/spec/integration/outputs/metrics_spec.rb +70 -0
  52. data/spec/integration/outputs/no_es_on_startup_spec.rb +58 -0
  53. data/spec/integration/outputs/painless_update_spec.rb +189 -0
  54. data/spec/integration/outputs/parent_spec.rb +102 -0
  55. data/spec/integration/outputs/retry_spec.rb +169 -0
  56. data/spec/integration/outputs/routing_spec.rb +61 -0
  57. data/spec/integration/outputs/sniffer_spec.rb +133 -0
  58. data/spec/integration/outputs/templates_5x_spec.rb +98 -0
  59. data/spec/integration/outputs/templates_spec.rb +98 -0
  60. data/spec/integration/outputs/update_spec.rb +116 -0
  61. data/spec/support/elasticsearch/api/actions/delete_ilm_policy.rb +19 -0
  62. data/spec/support/elasticsearch/api/actions/get_alias.rb +18 -0
  63. data/spec/support/elasticsearch/api/actions/get_ilm_policy.rb +18 -0
  64. data/spec/support/elasticsearch/api/actions/put_alias.rb +24 -0
  65. data/spec/support/elasticsearch/api/actions/put_ilm_policy.rb +25 -0
  66. data/spec/unit/http_client_builder_spec.rb +185 -0
  67. data/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb +149 -0
  68. data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +274 -0
  69. data/spec/unit/outputs/elasticsearch/http_client_spec.rb +250 -0
  70. data/spec/unit/outputs/elasticsearch/template_manager_spec.rb +25 -0
  71. data/spec/unit/outputs/elasticsearch_proxy_spec.rb +72 -0
  72. data/spec/unit/outputs/elasticsearch_spec.rb +675 -0
  73. data/spec/unit/outputs/elasticsearch_ssl_spec.rb +82 -0
  74. data/spec/unit/outputs/error_whitelist_spec.rb +54 -0
  75. metadata +300 -0
@@ -0,0 +1,178 @@
1
+ require_relative "../../../spec/es_spec_helper"
2
+ require "logstash/outputs/elasticsearch"
3
+
4
+ describe "TARGET_BULK_BYTES", :integration => true do
5
+ let(:target_bulk_bytes) { LogStash::Outputs::ElasticSearch::TARGET_BULK_BYTES }
6
+ let(:event_count) { 1000 }
7
+ let(:events) { event_count.times.map { event }.to_a }
8
+ let(:config) {
9
+ {
10
+ "hosts" => get_host_port,
11
+ "index" => index
12
+ }
13
+ }
14
+ let(:index) { 10.times.collect { rand(10).to_s }.join("") }
15
+ let(:type) { ESHelper.es_version_satisfies?("< 7") ? "doc" : "_doc" }
16
+
17
+ subject { LogStash::Outputs::ElasticSearch.new(config) }
18
+
19
+ before do
20
+ subject.register
21
+ allow(subject.client).to receive(:bulk_send).with(any_args).and_call_original
22
+ subject.multi_receive(events)
23
+ end
24
+
25
+ describe "batches that are too large for one" do
26
+ let(:event) { LogStash::Event.new("message" => "a " * (((target_bulk_bytes/2) / event_count)+1)) }
27
+
28
+ it "should send in two batches" do
29
+ expect(subject.client).to have_received(:bulk_send).twice do |payload|
30
+ expect(payload.size).to be <= target_bulk_bytes
31
+ end
32
+ end
33
+
34
+ describe "batches that fit in one" do
35
+ # Normally you'd want to generate a request that's just 1 byte below the limit, but it's
36
+ # impossible to know how many bytes an event will serialize as with bulk proto overhead
37
+ let(:event) { LogStash::Event.new("message" => "a") }
38
+
39
+ it "should send in one batch" do
40
+ expect(subject.client).to have_received(:bulk_send).once do |payload|
41
+ expect(payload.size).to be <= target_bulk_bytes
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "indexing" do
49
+ let(:event) { LogStash::Event.new("message" => "Hello World!", "type" => type) }
50
+ let(:index) { 10.times.collect { rand(10).to_s }.join("") }
51
+ let(:type) { ESHelper.es_version_satisfies?("< 7") ? "doc" : "_doc" }
52
+ let(:event_count) { 1 + rand(2) }
53
+ let(:config) { "not implemented" }
54
+ let(:events) { event_count.times.map { event }.to_a }
55
+ subject { LogStash::Outputs::ElasticSearch.new(config) }
56
+
57
+ let(:es_url) { "http://#{get_host_port}" }
58
+ let(:index_url) {"#{es_url}/#{index}"}
59
+ let(:http_client_options) { {} }
60
+ let(:http_client) do
61
+ Manticore::Client.new(http_client_options)
62
+ end
63
+
64
+ before do
65
+ subject.register
66
+ subject.multi_receive([])
67
+ end
68
+
69
+ shared_examples "an indexer" do |secure|
70
+ it "ships events" do
71
+ subject.multi_receive(events)
72
+
73
+ http_client.post("#{es_url}/_refresh").call
74
+
75
+ response = http_client.get("#{index_url}/_count?q=*")
76
+ result = LogStash::Json.load(response.body)
77
+ cur_count = result["count"]
78
+ expect(cur_count).to eq(event_count)
79
+
80
+ response = http_client.get("#{index_url}/_search?q=*&size=1000")
81
+ result = LogStash::Json.load(response.body)
82
+ result["hits"]["hits"].each do |doc|
83
+ expect(doc["_type"]).to eq(type) if ESHelper.es_version_satisfies?(">= 6", "< 8")
84
+ expect(doc).not_to include("_type") if ESHelper.es_version_satisfies?(">= 8")
85
+ expect(doc["_index"]).to eq(index)
86
+ end
87
+ end
88
+
89
+ it "sets the correct content-type header" do
90
+ expected_manticore_opts = {:headers => {"Content-Type" => "application/json"}, :body => anything}
91
+ if secure
92
+ expected_manticore_opts = {
93
+ :headers => {"Content-Type" => "application/json"},
94
+ :body => anything,
95
+ :auth => {
96
+ :user => user,
97
+ :password => password,
98
+ :eager => true
99
+ }}
100
+ end
101
+ expect(subject.client.pool.adapter.client).to receive(:send).
102
+ with(anything, anything, expected_manticore_opts).at_least(:once).
103
+ and_call_original
104
+ subject.multi_receive(events)
105
+ end
106
+ end
107
+
108
+ describe "an indexer with custom index_type", :integration => true do
109
+ let(:config) {
110
+ {
111
+ "hosts" => get_host_port,
112
+ "index" => index
113
+ }
114
+ }
115
+ it_behaves_like("an indexer")
116
+ end
117
+
118
+ describe "an indexer with no type value set (default to doc)", :integration => true do
119
+ let(:type) { ESHelper.es_version_satisfies?("< 7") ? "doc" : "_doc" }
120
+ let(:config) {
121
+ {
122
+ "hosts" => get_host_port,
123
+ "index" => index
124
+ }
125
+ }
126
+ it_behaves_like("an indexer")
127
+ end
128
+
129
+ describe "a secured indexer", :secure_integration => true do
130
+ let(:user) { "simpleuser" }
131
+ let(:password) { "abc123" }
132
+ let(:cacert) { "spec/fixtures/test_certs/test.crt" }
133
+ let(:es_url) {"https://elasticsearch:9200"}
134
+ let(:config) do
135
+ {
136
+ "hosts" => ["elasticsearch:9200"],
137
+ "user" => user,
138
+ "password" => password,
139
+ "ssl" => true,
140
+ "cacert" => "spec/fixtures/test_certs/test.crt",
141
+ "index" => index
142
+ }
143
+ end
144
+ let(:http_client_options) do
145
+ {
146
+ :auth => {
147
+ :user => user,
148
+ :password => password
149
+ },
150
+ :ssl => {
151
+ :enabled => true,
152
+ :ca_file => cacert
153
+ }
154
+ }
155
+ end
156
+ it_behaves_like("an indexer", true)
157
+
158
+ describe "with a password requiring escaping" do
159
+ let(:user) { "f@ncyuser" }
160
+ let(:password) { "ab%12#" }
161
+
162
+ include_examples("an indexer", true)
163
+ end
164
+
165
+ describe "with a user/password requiring escaping in the URL" do
166
+ let(:config) do
167
+ {
168
+ "hosts" => ["https://#{CGI.escape(user)}:#{CGI.escape(password)}@elasticsearch:9200"],
169
+ "ssl" => true,
170
+ "cacert" => "spec/fixtures/test_certs/test.crt",
171
+ "index" => index
172
+ }
173
+ end
174
+
175
+ include_examples("an indexer", true)
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,102 @@
1
+ require_relative "../../../spec/es_spec_helper"
2
+ require "logstash/outputs/elasticsearch"
3
+
4
+ if ESHelper.es_version_satisfies?(">= 2")
5
+ describe "Versioned indexing", :integration => true do
6
+ require "logstash/outputs/elasticsearch"
7
+
8
+ let(:es) { get_client }
9
+
10
+ before :each do
11
+ # Delete all templates first.
12
+ # Clean ES of data before we start.
13
+ es.indices.delete_template(:name => "*")
14
+ # This can fail if there are no indexes, ignore failure.
15
+ es.indices.delete(:index => "*") rescue nil
16
+ es.indices.refresh
17
+ end
18
+
19
+ context "when index only" do
20
+ subject { LogStash::Outputs::ElasticSearch.new(settings) }
21
+
22
+ before do
23
+ subject.register
24
+ end
25
+
26
+ describe "unversioned output" do
27
+ let(:settings) do
28
+ {
29
+ "manage_template" => true,
30
+ "index" => "logstash-index",
31
+ "template_overwrite" => true,
32
+ "hosts" => get_host_port(),
33
+ "action" => "index",
34
+ "script_lang" => "groovy",
35
+ "document_id" => "%{my_id}"
36
+ }
37
+ end
38
+
39
+ it "should default to ES version" do
40
+ subject.multi_receive([LogStash::Event.new("my_id" => "123", "message" => "foo")])
41
+ r = es.get(:index => 'logstash-index', :type => doc_type, :id => "123", :refresh => true)
42
+ expect(r["_version"]).to eq(1)
43
+ expect(r["_source"]["message"]).to eq('foo')
44
+ subject.multi_receive([LogStash::Event.new("my_id" => "123", "message" => "foobar")])
45
+ r2 = es.get(:index => 'logstash-index', :type => doc_type, :id => "123", :refresh => true)
46
+ expect(r2["_version"]).to eq(2)
47
+ expect(r2["_source"]["message"]).to eq('foobar')
48
+ end
49
+ end
50
+
51
+ describe "versioned output" do
52
+ let(:settings) do
53
+ {
54
+ "manage_template" => true,
55
+ "index" => "logstash-index",
56
+ "template_overwrite" => true,
57
+ "hosts" => get_host_port(),
58
+ "action" => "index",
59
+ "script_lang" => "groovy",
60
+ "document_id" => "%{my_id}",
61
+ "version" => "%{my_version}",
62
+ "version_type" => "external",
63
+ }
64
+ end
65
+
66
+ it "should respect the external version" do
67
+ id = "ev1"
68
+ subject.multi_receive([LogStash::Event.new("my_id" => id, "my_version" => "99", "message" => "foo")])
69
+ r = es.get(:index => 'logstash-index', :type => doc_type, :id => id, :refresh => true)
70
+ expect(r["_version"]).to eq(99)
71
+ expect(r["_source"]["message"]).to eq('foo')
72
+ end
73
+
74
+ it "should ignore non-monotonic external version updates" do
75
+ id = "ev2"
76
+ subject.multi_receive([LogStash::Event.new("my_id" => id, "my_version" => "99", "message" => "foo")])
77
+ r = es.get(:index => 'logstash-index', :type => doc_type, :id => id, :refresh => true)
78
+ expect(r["_version"]).to eq(99)
79
+ expect(r["_source"]["message"]).to eq('foo')
80
+
81
+ subject.multi_receive([LogStash::Event.new("my_id" => id, "my_version" => "98", "message" => "foo")])
82
+ r2 = es.get(:index => 'logstash-index', :type => doc_type, :id => id, :refresh => true)
83
+ expect(r2["_version"]).to eq(99)
84
+ expect(r2["_source"]["message"]).to eq('foo')
85
+ end
86
+
87
+ it "should commit monotonic external version updates" do
88
+ id = "ev3"
89
+ subject.multi_receive([LogStash::Event.new("my_id" => id, "my_version" => "99", "message" => "foo")])
90
+ r = es.get(:index => 'logstash-index', :type => doc_type, :id => id, :refresh => true)
91
+ expect(r["_version"]).to eq(99)
92
+ expect(r["_source"]["message"]).to eq('foo')
93
+
94
+ subject.multi_receive([LogStash::Event.new("my_id" => id, "my_version" => "100", "message" => "foo")])
95
+ r2 = es.get(:index => 'logstash-index', :type => doc_type, :id => id, :refresh => true)
96
+ expect(r2["_version"]).to eq(100)
97
+ expect(r2["_source"]["message"]).to eq('foo')
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,74 @@
1
+ require_relative "../../../spec/es_spec_helper"
2
+
3
+ if ESHelper.es_version_satisfies?(">= 5")
4
+ describe "Ingest pipeline execution behavior", :integration => true do
5
+ subject! do
6
+ require "logstash/outputs/elasticsearch"
7
+ settings = {
8
+ "hosts" => "#{get_host_port()}",
9
+ "pipeline" => "apache-logs"
10
+ }
11
+ next LogStash::Outputs::ElasticSearch.new(settings)
12
+ end
13
+
14
+ let(:http_client) { Manticore::Client.new }
15
+ let(:ingest_url) { "http://#{get_host_port()}/_ingest/pipeline/apache-logs" }
16
+ let(:apache_logs_pipeline) { '
17
+ {
18
+ "description" : "Pipeline to parse Apache logs",
19
+ "processors" : [
20
+ {
21
+ "grok": {
22
+ "field": "message",
23
+ "patterns": ["%{COMBINEDAPACHELOG}"]
24
+ }
25
+ }
26
+ ]
27
+ }'
28
+ }
29
+
30
+ before :each do
31
+ # Delete all templates first.
32
+ require "elasticsearch"
33
+
34
+ # Clean ES of data before we start.
35
+ @es = get_client
36
+ @es.indices.delete_template(:name => "*")
37
+
38
+ # This can fail if there are no indexes, ignore failure.
39
+ @es.indices.delete(:index => "*") rescue nil
40
+
41
+ # delete existing ingest pipeline
42
+ http_client.delete(ingest_url).call
43
+
44
+ # register pipeline
45
+ http_client.put(ingest_url, :body => apache_logs_pipeline, :headers => {"Content-Type" => "application/json" }).call
46
+
47
+ #TODO: Use esclient
48
+ #@es.ingest.put_pipeline :id => 'apache_pipeline', :body => pipeline_defintion
49
+
50
+ subject.register
51
+ subject.multi_receive([LogStash::Event.new("message" => '183.60.215.50 - - [01/Jun/2015:18:00:00 +0000] "GET /scripts/netcat-webserver HTTP/1.1" 200 182 "-" "Mozilla/5.0 (compatible; EasouSpider; +http://www.easou.com/search/spider.html)"')])
52
+ @es.indices.refresh
53
+
54
+ #Wait or fail until everything's indexed.
55
+ Stud::try(20.times) do
56
+ r = @es.search
57
+ expect(r).to have_hits(1)
58
+ end
59
+ end
60
+
61
+ it "indexes using the proper pipeline" do
62
+ results = @es.search(:index => 'logstash-*', :q => "message:\"netcat\"")
63
+ expect(results).to have_hits(1)
64
+ expect(results["hits"]["hits"][0]["_source"]["response"]).to eq("200")
65
+ expect(results["hits"]["hits"][0]["_source"]["bytes"]).to eq("182")
66
+ expect(results["hits"]["hits"][0]["_source"]["verb"]).to eq("GET")
67
+ expect(results["hits"]["hits"][0]["_source"]["request"]).to eq("/scripts/netcat-webserver")
68
+ expect(results["hits"]["hits"][0]["_source"]["auth"]).to eq("-")
69
+ expect(results["hits"]["hits"][0]["_source"]["ident"]).to eq("-")
70
+ expect(results["hits"]["hits"][0]["_source"]["clientip"]).to eq("183.60.215.50")
71
+ expect(results["hits"]["hits"][0]["_source"]["junkfieldaaaa"]).to eq(nil)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,70 @@
1
+ require_relative "../../../spec/es_spec_helper"
2
+
3
+ describe "metrics", :integration => true do
4
+ subject! do
5
+ require "logstash/outputs/elasticsearch"
6
+ settings = {
7
+ "manage_template" => false,
8
+ "hosts" => "#{get_host_port()}"
9
+ }
10
+ plugin = LogStash::Outputs::ElasticSearch.new(settings)
11
+ end
12
+
13
+ let(:metric) { subject.metric }
14
+ let(:bulk_request_metrics) { subject.instance_variable_get(:@bulk_request_metrics) }
15
+ let(:document_level_metrics) { subject.instance_variable_get(:@document_level_metrics) }
16
+
17
+ before :each do
18
+ require "elasticsearch"
19
+
20
+ # Clean ES of data before we start.
21
+ @es = get_client
22
+ @es.indices.delete_template(:name => "*")
23
+
24
+ # This can fail if there are no indexes, ignore failure.
25
+ @es.indices.delete(:index => "*") rescue nil
26
+ #@es.indices.refresh
27
+ subject.register
28
+ end
29
+
30
+ context "after a succesful bulk insert" do
31
+ let(:bulk) { [
32
+ LogStash::Event.new("message" => "sample message here"),
33
+ LogStash::Event.new("somemessage" => { "message" => "sample nested message here" }),
34
+ LogStash::Event.new("somevalue" => 100),
35
+ LogStash::Event.new("somevalue" => 10),
36
+ LogStash::Event.new("somevalue" => 1),
37
+ LogStash::Event.new("country" => "us"),
38
+ LogStash::Event.new("country" => "at"),
39
+ LogStash::Event.new("geoip" => { "location" => [ 0.0, 0.0 ] })
40
+ ]}
41
+
42
+ it "increases successful bulk request metric" do
43
+ expect(bulk_request_metrics).to receive(:increment).with(:successes).once
44
+ subject.multi_receive(bulk)
45
+ end
46
+
47
+ it "increases number of successful inserted documents" do
48
+ expect(document_level_metrics).to receive(:increment).with(:successes, bulk.size).once
49
+ subject.multi_receive(bulk)
50
+ end
51
+ end
52
+
53
+ context "after a bulk insert that generates errors" do
54
+ let(:bulk) { [
55
+ LogStash::Event.new("message" => "sample message here"),
56
+ LogStash::Event.new("message" => { "message" => "sample nested message here" }),
57
+ ]}
58
+ it "increases bulk request with error metric" do
59
+ expect(bulk_request_metrics).to receive(:increment).with(:with_errors).once
60
+ expect(bulk_request_metrics).to_not receive(:increment).with(:successes)
61
+ subject.multi_receive(bulk)
62
+ end
63
+
64
+ it "increases number of successful and non retryable documents" do
65
+ expect(document_level_metrics).to receive(:increment).with(:non_retryable_failures).once
66
+ expect(document_level_metrics).to receive(:increment).with(:successes).once
67
+ subject.multi_receive(bulk)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,58 @@
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
+ })
17
+ }
18
+
19
+ before :each do
20
+ # Delete all templates first.
21
+ require "elasticsearch"
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(::LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError.new(StandardError.new, "big fail"))
37
+ subject.register
38
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:get_es_version).and_return(ESHelper.es_version)
39
+ subject.multi_receive([event1, event2])
40
+ @es.indices.refresh
41
+ r = @es.search
42
+ expect(r).to have_hits(2)
43
+ end
44
+
45
+ it 'should ingest events when Elasticsearch recovers after documents are sent' do
46
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:get_es_version).and_raise(::LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError.new(StandardError.new, "big fail"))
47
+ subject.register
48
+ Thread.new do
49
+ sleep 4
50
+ allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:get_es_version).and_return(ESHelper.es_version)
51
+ end
52
+ subject.multi_receive([event1, event2])
53
+ @es.indices.refresh
54
+ r = @es.search
55
+ expect(r).to have_hits(2)
56
+ end
57
+
58
+ end
@@ -0,0 +1,189 @@
1
+ require_relative "../../../spec/es_spec_helper"
2
+
3
+ if ESHelper.es_version_satisfies?(">= 5")
4
+ describe "Update actions using painless scripts", :integration => true, :update_tests => 'painless' do
5
+ require "logstash/outputs/elasticsearch"
6
+
7
+ def get_es_output( options={} )
8
+ settings = {
9
+ "manage_template" => true,
10
+ "index" => "logstash-update",
11
+ "template_overwrite" => true,
12
+ "hosts" => get_host_port(),
13
+ "action" => "update"
14
+ }
15
+ if ESHelper.es_version_satisfies?('<6')
16
+ settings.merge!({"script_lang" => "painless"})
17
+ end
18
+ LogStash::Outputs::ElasticSearch.new(settings.merge!(options))
19
+ end
20
+
21
+ before :each do
22
+ @es = get_client
23
+ # Delete all templates first.
24
+ # Clean ES of data before we start.
25
+ @es.indices.delete_template(:name => "*")
26
+ # This can fail if there are no indexes, ignore failure.
27
+ @es.indices.delete(:index => "*") rescue nil
28
+ @es.index(
29
+ :index => 'logstash-update',
30
+ :type => doc_type,
31
+ :id => "123",
32
+ :body => { :message => 'Test', :counter => 1 }
33
+ )
34
+ @es.indices.refresh
35
+ end
36
+
37
+ context "scripted updates" do
38
+ if ESHelper.es_version_satisfies?('<6')
39
+ context 'with file based scripts' do
40
+ it "should increment a counter with event/doc 'count' variable" do
41
+ subject = get_es_output({ 'document_id' => "123", 'script' => 'scripted_update', 'script_type' => 'file' })
42
+ subject.register
43
+ subject.multi_receive([LogStash::Event.new("count" => 2)])
44
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "123", :refresh => true)
45
+ expect(r["_source"]["counter"]).to eq(3)
46
+ end
47
+
48
+ it "should increment a counter with event/doc '[data][count]' nested variable" do
49
+ subject = get_es_output({ 'document_id' => "123", 'script' => 'scripted_update_nested', 'script_type' => 'file' })
50
+ subject.register
51
+ subject.multi_receive([LogStash::Event.new("data" => { "count" => 3 })])
52
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "123", :refresh => true)
53
+ expect(r["_source"]["counter"]).to eq(4)
54
+ end
55
+ end
56
+ end
57
+
58
+ it "should increment a counter with event/doc 'count' variable with inline script" do
59
+ subject = get_es_output({
60
+ 'document_id' => "123",
61
+ 'script' => 'ctx._source.counter += params.event.counter',
62
+ 'script_type' => 'inline'
63
+ })
64
+ subject.register
65
+ subject.multi_receive([LogStash::Event.new("counter" => 3 )])
66
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "123", :refresh => true)
67
+ expect(r["_source"]["counter"]).to eq(4)
68
+ end
69
+
70
+ it "should increment a counter with event/doc 'count' variable with event/doc as upsert and inline script" do
71
+ subject = get_es_output({
72
+ 'document_id' => "123",
73
+ 'doc_as_upsert' => true,
74
+ 'script' => 'if( ctx._source.containsKey("counter") ){ ctx._source.counter += params.event.counter; } else { ctx._source.counter = params.event.counter; }',
75
+ 'script_type' => 'inline'
76
+ })
77
+ subject.register
78
+ subject.multi_receive([LogStash::Event.new("counter" => 3 )])
79
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "123", :refresh => true)
80
+ expect(r["_source"]["counter"]).to eq(4)
81
+ end
82
+
83
+ it "should, with new doc, set a counter with event/doc 'count' variable with event/doc as upsert and inline script" do
84
+ subject = get_es_output({
85
+ 'document_id' => "456",
86
+ 'doc_as_upsert' => true,
87
+ 'script' => 'if( ctx._source.containsKey("counter") ){ ctx._source.counter += params.event.counter; } else { ctx._source.counter = params.event.counter; }',
88
+ 'script_type' => 'inline'
89
+ })
90
+ subject.register
91
+ subject.multi_receive([LogStash::Event.new("counter" => 3 )])
92
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
93
+ expect(r["_source"]["counter"]).to eq(3)
94
+ end
95
+
96
+ context 'with an indexed script' do
97
+ it "should increment a counter with event/doc 'count' variable with indexed script" do
98
+ if ESHelper.es_version_satisfies?('<6')
99
+ @es.perform_request(:put, "_scripts/painless/indexed_update", {}, {"script" => "ctx._source.counter += params.event.count" })
100
+ else
101
+ @es.perform_request(:put, "_scripts/indexed_update", {}, {"script" => {"source" => "ctx._source.counter += params.event.count", "lang" => "painless"}})
102
+ end
103
+
104
+ plugin_parameters = {
105
+ 'document_id' => "123",
106
+ 'script' => 'indexed_update',
107
+ 'script_type' => 'indexed'
108
+ }
109
+
110
+ if ESHelper.es_version_satisfies?('>= 6.0.0')
111
+ plugin_parameters.merge!('script_lang' => '')
112
+ end
113
+
114
+ subject = get_es_output(plugin_parameters)
115
+ subject.register
116
+ subject.multi_receive([LogStash::Event.new("count" => 4 )])
117
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "123", :refresh => true)
118
+ expect(r["_source"]["counter"]).to eq(5)
119
+ end
120
+ end
121
+ end
122
+
123
+ context "when update with upsert" do
124
+ it "should create new documents with provided upsert" do
125
+ subject = get_es_output({ 'document_id' => "456", 'upsert' => '{"message": "upsert message"}' })
126
+ subject.register
127
+ subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
128
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
129
+ expect(r["_source"]["message"]).to eq('upsert message')
130
+ end
131
+
132
+ it "should create new documents with event/doc as upsert" do
133
+ subject = get_es_output({ 'document_id' => "456", 'doc_as_upsert' => true })
134
+ subject.register
135
+ subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
136
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
137
+ expect(r["_source"]["message"]).to eq('sample message here')
138
+ end
139
+
140
+ it "should fail on documents with event/doc as upsert at external version" do
141
+ subject = get_es_output({ 'document_id' => "456", 'doc_as_upsert' => true, 'version' => 999, "version_type" => "external" })
142
+ expect { subject.register }.to raise_error(LogStash::ConfigurationError)
143
+ end
144
+ end
145
+
146
+ context "updates with scripted upsert" do
147
+ if ESHelper.es_version_satisfies?('<6')
148
+ context 'with file based scripts' do
149
+ it "should create new documents with upsert content" do
150
+ subject = get_es_output({ 'document_id' => "456", 'script' => 'scripted_update', 'upsert' => '{"message": "upsert message"}', 'script_type' => 'file' })
151
+ subject.register
152
+ subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
153
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
154
+ expect(r["_source"]["message"]).to eq('upsert message')
155
+ end
156
+
157
+ it "should create new documents with event/doc as script params" do
158
+ subject = get_es_output({ 'document_id' => "456", 'script' => 'scripted_upsert', 'scripted_upsert' => true, 'script_type' => 'file' })
159
+ subject.register
160
+ subject.multi_receive([LogStash::Event.new("counter" => 1)])
161
+ @es.indices.refresh
162
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
163
+ expect(r["_source"]["counter"]).to eq(1)
164
+ end
165
+ end
166
+ end
167
+
168
+ context 'with an inline script' do
169
+ it "should create new documents with upsert content" do
170
+ subject = get_es_output({ 'document_id' => "456", 'script' => 'ctx._source.counter = params.event.counter', 'upsert' => '{"message": "upsert message"}', 'script_type' => 'inline' })
171
+ subject.register
172
+
173
+ subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
174
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
175
+ expect(r["_source"]["message"]).to eq('upsert message')
176
+ end
177
+
178
+ it "should create new documents with event/doc as script params" do
179
+ subject = get_es_output({ 'document_id' => "456", 'script' => 'ctx._source.counter = params.event.counter', 'scripted_upsert' => true, 'script_type' => 'inline' })
180
+ subject.register
181
+ subject.multi_receive([LogStash::Event.new("counter" => 1)])
182
+ @es.indices.refresh
183
+ r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
184
+ expect(r["_source"]["counter"]).to eq(1)
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end