logstash-output-elasticsearch-test 10.3.0-x86_64-linux

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