logstash-output-elasticsearch 10.8.6-java → 11.0.3-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/docs/index.asciidoc +132 -22
- data/lib/logstash/outputs/elasticsearch.rb +125 -64
- data/lib/logstash/outputs/elasticsearch/data_stream_support.rb +233 -0
- data/lib/logstash/outputs/elasticsearch/http_client.rb +9 -7
- data/lib/logstash/outputs/elasticsearch/http_client/pool.rb +49 -62
- data/lib/logstash/outputs/elasticsearch/ilm.rb +13 -45
- data/lib/logstash/outputs/elasticsearch/license_checker.rb +26 -23
- data/lib/logstash/outputs/elasticsearch/template_manager.rb +4 -6
- data/lib/logstash/outputs/elasticsearch/templates/ecs-v1/elasticsearch-8x.json +1 -0
- data/lib/logstash/plugin_mixins/elasticsearch/api_configs.rb +157 -153
- data/lib/logstash/plugin_mixins/elasticsearch/common.rb +71 -58
- data/logstash-output-elasticsearch.gemspec +3 -3
- data/spec/es_spec_helper.rb +7 -12
- data/spec/fixtures/_nodes/{5x_6x.json → 6x.json} +5 -5
- data/spec/integration/outputs/compressed_indexing_spec.rb +47 -46
- data/spec/integration/outputs/data_stream_spec.rb +61 -0
- data/spec/integration/outputs/delete_spec.rb +49 -51
- data/spec/integration/outputs/ilm_spec.rb +236 -248
- data/spec/integration/outputs/index_spec.rb +5 -2
- data/spec/integration/outputs/index_version_spec.rb +78 -82
- data/spec/integration/outputs/ingest_pipeline_spec.rb +58 -58
- data/spec/integration/outputs/painless_update_spec.rb +74 -164
- data/spec/integration/outputs/parent_spec.rb +67 -75
- data/spec/integration/outputs/retry_spec.rb +6 -6
- data/spec/integration/outputs/sniffer_spec.rb +15 -54
- data/spec/integration/outputs/templates_spec.rb +79 -81
- data/spec/integration/outputs/update_spec.rb +99 -101
- data/spec/spec_helper.rb +10 -0
- data/spec/unit/outputs/elasticsearch/data_stream_support_spec.rb +528 -0
- data/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb +1 -0
- data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +36 -29
- data/spec/unit/outputs/elasticsearch/http_client_spec.rb +2 -3
- data/spec/unit/outputs/elasticsearch/template_manager_spec.rb +10 -12
- data/spec/unit/outputs/elasticsearch_proxy_spec.rb +1 -2
- data/spec/unit/outputs/elasticsearch_spec.rb +176 -41
- data/spec/unit/outputs/elasticsearch_ssl_spec.rb +1 -2
- data/spec/unit/outputs/error_whitelist_spec.rb +3 -2
- data/spec/unit/outputs/license_check_spec.rb +0 -16
- metadata +29 -36
- data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-2x.json +0 -95
- data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-5x.json +0 -46
- data/spec/fixtures/_nodes/2x_1x.json +0 -27
- data/spec/fixtures/scripts/groovy/scripted_update.groovy +0 -2
- data/spec/fixtures/scripts/groovy/scripted_update_nested.groovy +0 -2
- data/spec/fixtures/scripts/groovy/scripted_upsert.groovy +0 -2
- data/spec/integration/outputs/groovy_update_spec.rb +0 -150
- data/spec/integration/outputs/templates_5x_spec.rb +0 -98
@@ -5,7 +5,7 @@ module LogStash; module PluginMixins; module ElasticSearch
|
|
5
5
|
|
6
6
|
# This module defines common methods that can be reused by alternate elasticsearch output plugins such as the elasticsearch_data_streams output.
|
7
7
|
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :hosts
|
9
9
|
|
10
10
|
# These codes apply to documents, not at the request level
|
11
11
|
DOC_DLQ_CODES = [400, 404]
|
@@ -31,7 +31,7 @@ module LogStash; module PluginMixins; module ElasticSearch
|
|
31
31
|
if @proxy.eql?('')
|
32
32
|
@logger.warn "Supplied proxy setting (proxy => '') has no effect"
|
33
33
|
end
|
34
|
-
|
34
|
+
::LogStash::Outputs::ElasticSearch::HttpClientBuilder.build(@logger, @hosts, params)
|
35
35
|
end
|
36
36
|
|
37
37
|
def validate_authentication
|
@@ -115,6 +115,15 @@ module LogStash; module PluginMixins; module ElasticSearch
|
|
115
115
|
end
|
116
116
|
private :parse_user_password_from_cloud_auth
|
117
117
|
|
118
|
+
# Plugin initialization extension point (after a successful ES connection).
|
119
|
+
def finish_register
|
120
|
+
end
|
121
|
+
protected :finish_register
|
122
|
+
|
123
|
+
def last_es_version
|
124
|
+
client.last_es_version
|
125
|
+
end
|
126
|
+
|
118
127
|
def maximum_seen_major_version
|
119
128
|
client.maximum_seen_major_version
|
120
129
|
end
|
@@ -126,25 +135,24 @@ module LogStash; module PluginMixins; module ElasticSearch
|
|
126
135
|
# launch a thread that waits for an initial successful connection to the ES cluster to call the given block
|
127
136
|
# @param block [Proc] the block to execute upon initial successful connection
|
128
137
|
# @return [Thread] the successful connection wait thread
|
129
|
-
def
|
138
|
+
def after_successful_connection(&block)
|
130
139
|
Thread.new do
|
131
140
|
sleep_interval = @retry_initial_interval
|
132
141
|
until successful_connection? || @stopping.true?
|
133
|
-
@logger.debug("Waiting for connectivity to Elasticsearch cluster
|
134
|
-
|
135
|
-
sleep_interval = next_sleep_interval(sleep_interval)
|
142
|
+
@logger.debug("Waiting for connectivity to Elasticsearch cluster, retrying in #{sleep_interval}s")
|
143
|
+
sleep_interval = sleep_for_interval(sleep_interval)
|
136
144
|
end
|
137
145
|
block.call if successful_connection?
|
138
146
|
end
|
139
147
|
end
|
148
|
+
private :after_successful_connection
|
140
149
|
|
141
150
|
def discover_cluster_uuid
|
142
151
|
return unless defined?(plugin_metadata)
|
143
152
|
cluster_info = client.get('/')
|
144
153
|
plugin_metadata.set(:cluster_uuid, cluster_info['cluster_uuid'])
|
145
154
|
rescue => e
|
146
|
-
|
147
|
-
# @logger.error("Unable to retrieve elasticsearch cluster uuid", error => e.message)
|
155
|
+
@logger.error("Unable to retrieve Elasticsearch cluster uuid", message: e.message, exception: e.class, backtrace: e.backtrace)
|
148
156
|
end
|
149
157
|
|
150
158
|
def retrying_submit(actions)
|
@@ -159,13 +167,11 @@ module LogStash; module PluginMixins; module ElasticSearch
|
|
159
167
|
begin
|
160
168
|
submit_actions = submit(submit_actions)
|
161
169
|
if submit_actions && submit_actions.size > 0
|
162
|
-
@logger.info("Retrying individual bulk actions that failed or were rejected by the previous bulk request
|
170
|
+
@logger.info("Retrying individual bulk actions that failed or were rejected by the previous bulk request", count: submit_actions.size)
|
163
171
|
end
|
164
172
|
rescue => e
|
165
|
-
@logger.error("Encountered an unexpected error submitting a bulk request
|
166
|
-
|
167
|
-
:class => e.class.name,
|
168
|
-
:backtrace => e.backtrace)
|
173
|
+
@logger.error("Encountered an unexpected error submitting a bulk request, will retry",
|
174
|
+
message: e.message, exception: e.class, backtrace: e.backtrace)
|
169
175
|
end
|
170
176
|
|
171
177
|
# Everything was a success!
|
@@ -173,21 +179,42 @@ module LogStash; module PluginMixins; module ElasticSearch
|
|
173
179
|
|
174
180
|
# If we're retrying the action sleep for the recommended interval
|
175
181
|
# Double the interval for the next time through to achieve exponential backoff
|
176
|
-
|
177
|
-
sleep_interval = next_sleep_interval(sleep_interval)
|
182
|
+
sleep_interval = sleep_for_interval(sleep_interval)
|
178
183
|
end
|
179
184
|
end
|
180
185
|
|
181
186
|
def sleep_for_interval(sleep_interval)
|
182
|
-
|
187
|
+
stoppable_sleep(sleep_interval)
|
183
188
|
next_sleep_interval(sleep_interval)
|
184
189
|
end
|
185
190
|
|
191
|
+
def stoppable_sleep(interval)
|
192
|
+
Stud.stoppable_sleep(interval) { @stopping.true? }
|
193
|
+
end
|
194
|
+
|
186
195
|
def next_sleep_interval(current_interval)
|
187
196
|
doubled = current_interval * 2
|
188
197
|
doubled > @retry_max_interval ? @retry_max_interval : doubled
|
189
198
|
end
|
190
199
|
|
200
|
+
def handle_dlq_status(message, action, status, response)
|
201
|
+
# To support bwc, we check if DLQ exists. otherwise we log and drop event (previous behavior)
|
202
|
+
if @dlq_writer
|
203
|
+
event, action = action.event, [action[0], action[1], action[2]]
|
204
|
+
# TODO: Change this to send a map with { :status => status, :action => action } in the future
|
205
|
+
@dlq_writer.write(event, "#{message} status: #{status}, action: #{action}, response: #{response}")
|
206
|
+
else
|
207
|
+
if dig_value(response, 'index', 'error', 'type') == 'invalid_index_name_exception'
|
208
|
+
level = :error
|
209
|
+
else
|
210
|
+
level = :warn
|
211
|
+
end
|
212
|
+
@logger.send level, message, status: status, action: action, response: response
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
private
|
217
|
+
|
191
218
|
def submit(actions)
|
192
219
|
bulk_response = safe_bulk(actions)
|
193
220
|
|
@@ -217,7 +244,7 @@ module LogStash; module PluginMixins; module ElasticSearch
|
|
217
244
|
action_type, action_props = response.first
|
218
245
|
|
219
246
|
status = action_props["status"]
|
220
|
-
|
247
|
+
error = action_props["error"]
|
221
248
|
action = actions[idx]
|
222
249
|
action_params = action[1]
|
223
250
|
|
@@ -230,7 +257,7 @@ module LogStash; module PluginMixins; module ElasticSearch
|
|
230
257
|
next
|
231
258
|
elsif DOC_CONFLICT_CODE == status
|
232
259
|
@document_level_metrics.increment(:non_retryable_failures)
|
233
|
-
@logger.warn "Failed action
|
260
|
+
@logger.warn "Failed action", status: status, action: action, response: response if log_failure_type?(error)
|
234
261
|
next
|
235
262
|
elsif DOC_DLQ_CODES.include?(status)
|
236
263
|
handle_dlq_status("Could not index event to Elasticsearch.", action, status, response)
|
@@ -239,7 +266,7 @@ module LogStash; module PluginMixins; module ElasticSearch
|
|
239
266
|
else
|
240
267
|
# only log what the user whitelisted
|
241
268
|
@document_level_metrics.increment(:retryable_failures)
|
242
|
-
@logger.info "
|
269
|
+
@logger.info "Retrying failed action", status: status, action: action, error: error if log_failure_type?(error)
|
243
270
|
actions_to_retry << action
|
244
271
|
end
|
245
272
|
end
|
@@ -247,40 +274,25 @@ module LogStash; module PluginMixins; module ElasticSearch
|
|
247
274
|
actions_to_retry
|
248
275
|
end
|
249
276
|
|
250
|
-
def
|
251
|
-
|
252
|
-
if @dlq_writer
|
253
|
-
# TODO: Change this to send a map with { :status => status, :action => action } in the future
|
254
|
-
@dlq_writer.write(action[2], "#{message} status: #{status}, action: #{action}, response: #{response}")
|
255
|
-
else
|
256
|
-
error_type = response.fetch('index', {}).fetch('error', {})['type']
|
257
|
-
if 'invalid_index_name_exception' == error_type
|
258
|
-
level = :error
|
259
|
-
else
|
260
|
-
level = :warn
|
261
|
-
end
|
262
|
-
@logger.send level, message, status: status, action: action, response: response
|
263
|
-
end
|
277
|
+
def log_failure_type?(failure)
|
278
|
+
!failure_type_logging_whitelist.include?(failure["type"])
|
264
279
|
end
|
265
280
|
|
266
281
|
# Rescue retryable errors during bulk submission
|
282
|
+
# @param actions a [action, params, event.to_hash] tuple
|
283
|
+
# @return response [Hash] which contains 'errors' and processed 'items' entries
|
267
284
|
def safe_bulk(actions)
|
268
285
|
sleep_interval = @retry_initial_interval
|
269
286
|
begin
|
270
|
-
|
271
|
-
response = @client.bulk(es_actions)
|
272
|
-
response
|
287
|
+
@client.bulk(actions) # returns { 'errors': ..., 'items': ... }
|
273
288
|
rescue ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError => e
|
274
289
|
# If we can't even connect to the server let's just print out the URL (:hosts is actually a URL)
|
275
290
|
# and let the user sort it out from there
|
276
291
|
@logger.error(
|
277
|
-
"Attempted to send a bulk request to
|
278
|
-
|
279
|
-
:error_message => e.message,
|
280
|
-
:class => e.class.name,
|
281
|
-
:will_retry_in_seconds => sleep_interval
|
292
|
+
"Attempted to send a bulk request but Elasticsearch appears to be unreachable or down",
|
293
|
+
message: e.message, exception: e.class, will_retry_in_seconds: sleep_interval
|
282
294
|
)
|
283
|
-
@logger.debug("Failed actions for last bad bulk request
|
295
|
+
@logger.debug? && @logger.debug("Failed actions for last bad bulk request", :actions => actions)
|
284
296
|
|
285
297
|
# We retry until there are no errors! Errors should all go to the retry queue
|
286
298
|
sleep_interval = sleep_for_interval(sleep_interval)
|
@@ -288,20 +300,19 @@ module LogStash; module PluginMixins; module ElasticSearch
|
|
288
300
|
retry unless @stopping.true?
|
289
301
|
rescue ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::NoConnectionAvailableError => e
|
290
302
|
@logger.error(
|
291
|
-
"Attempted to send a bulk request
|
292
|
-
|
293
|
-
:
|
294
|
-
:will_retry_in_seconds => sleep_interval
|
303
|
+
"Attempted to send a bulk request but there are no living connections in the pool " +
|
304
|
+
"(perhaps Elasticsearch is unreachable or down?)",
|
305
|
+
message: e.message, exception: e.class, will_retry_in_seconds: sleep_interval
|
295
306
|
)
|
296
|
-
|
297
|
-
sleep_interval =
|
307
|
+
|
308
|
+
sleep_interval = sleep_for_interval(sleep_interval)
|
298
309
|
@bulk_request_metrics.increment(:failures)
|
299
310
|
retry unless @stopping.true?
|
300
311
|
rescue ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError => e
|
301
312
|
@bulk_request_metrics.increment(:failures)
|
302
313
|
log_hash = {:code => e.response_code, :url => e.url.sanitized.to_s, :content_length => e.request_body.bytesize}
|
303
314
|
log_hash[:body] = e.response_body if @logger.debug? # Generally this is too verbose
|
304
|
-
message = "Encountered a retryable error
|
315
|
+
message = "Encountered a retryable error (will retry with exponential backoff)"
|
305
316
|
|
306
317
|
# We treat 429s as a special case because these really aren't errors, but
|
307
318
|
# rather just ES telling us to back off a bit, which we do.
|
@@ -315,17 +326,12 @@ module LogStash; module PluginMixins; module ElasticSearch
|
|
315
326
|
|
316
327
|
sleep_interval = sleep_for_interval(sleep_interval)
|
317
328
|
retry
|
318
|
-
rescue => e
|
319
|
-
# Stuff that should never happen
|
320
|
-
# For all other errors print out full connection issues
|
329
|
+
rescue => e # Stuff that should never happen - print out full connection issues
|
321
330
|
@logger.error(
|
322
|
-
"An unknown error occurred sending a bulk request to Elasticsearch
|
323
|
-
:
|
324
|
-
:error_class => e.class.name,
|
325
|
-
:backtrace => e.backtrace
|
331
|
+
"An unknown error occurred sending a bulk request to Elasticsearch (will retry indefinitely)",
|
332
|
+
message: e.message, exception: e.class, backtrace: e.backtrace
|
326
333
|
)
|
327
|
-
|
328
|
-
@logger.debug("Failed actions for last bad bulk request!", :actions => actions)
|
334
|
+
@logger.debug? && @logger.debug("Failed actions for last bad bulk request", :actions => actions)
|
329
335
|
|
330
336
|
sleep_interval = sleep_for_interval(sleep_interval)
|
331
337
|
@bulk_request_metrics.increment(:failures)
|
@@ -339,5 +345,12 @@ module LogStash; module PluginMixins; module ElasticSearch
|
|
339
345
|
respond_to?(:execution_context) && execution_context.respond_to?(:dlq_writer) &&
|
340
346
|
!execution_context.dlq_writer.inner_writer.is_a?(::LogStash::Util::DummyDeadLetterQueueWriter)
|
341
347
|
end
|
348
|
+
|
349
|
+
def dig_value(val, first_key, *rest_keys)
|
350
|
+
fail(TypeError, "cannot dig value from #{val.class}") unless val.kind_of?(Hash)
|
351
|
+
val = val[first_key]
|
352
|
+
return val if rest_keys.empty? || val == nil
|
353
|
+
dig_value(val, *rest_keys)
|
354
|
+
end
|
342
355
|
end
|
343
356
|
end; end; end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'logstash-output-elasticsearch'
|
3
|
-
s.version = '
|
3
|
+
s.version = '11.0.3'
|
4
4
|
|
5
5
|
s.licenses = ['apache-2.0']
|
6
6
|
s.summary = "Stores logs in Elasticsearch"
|
@@ -21,15 +21,15 @@ Gem::Specification.new do |s|
|
|
21
21
|
# Special flag to let us know this is actually a logstash plugin
|
22
22
|
s.metadata = { "logstash_plugin" => "true", "logstash_group" => "output" }
|
23
23
|
|
24
|
-
s.add_runtime_dependency "manticore", '>= 0.
|
24
|
+
s.add_runtime_dependency "manticore", '>= 0.7.1', '< 1.0.0'
|
25
25
|
s.add_runtime_dependency 'stud', ['>= 0.0.17', '~> 0.0']
|
26
|
-
s.add_runtime_dependency 'cabin', ['~> 0.6']
|
27
26
|
s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
|
28
27
|
s.add_runtime_dependency 'logstash-mixin-ecs_compatibility_support', '~>1.0'
|
29
28
|
|
30
29
|
s.add_development_dependency 'logstash-codec-plain'
|
31
30
|
s.add_development_dependency 'logstash-devutils'
|
32
31
|
s.add_development_dependency 'flores'
|
32
|
+
s.add_development_dependency 'cabin', ['~> 0.6']
|
33
33
|
# Still used in some specs, we should remove this ASAP
|
34
34
|
s.add_development_dependency 'elasticsearch'
|
35
35
|
end
|
data/spec/es_spec_helper.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative './spec_helper'
|
2
|
+
|
3
3
|
require 'elasticsearch'
|
4
4
|
require_relative "support/elasticsearch/api/actions/delete_ilm_policy"
|
5
5
|
require_relative "support/elasticsearch/api/actions/get_alias"
|
@@ -8,10 +8,7 @@ require_relative "support/elasticsearch/api/actions/get_ilm_policy"
|
|
8
8
|
require_relative "support/elasticsearch/api/actions/put_ilm_policy"
|
9
9
|
|
10
10
|
require 'json'
|
11
|
-
|
12
|
-
unless defined?(LogStash::OSS)
|
13
|
-
LogStash::OSS = ENV['DISTRIBUTION'] != "default"
|
14
|
-
end
|
11
|
+
require 'cabin'
|
15
12
|
|
16
13
|
module ESHelper
|
17
14
|
def get_host_port
|
@@ -23,7 +20,9 @@ module ESHelper
|
|
23
20
|
end
|
24
21
|
|
25
22
|
def get_client
|
26
|
-
Elasticsearch::Client.new(:hosts => [get_host_port])
|
23
|
+
Elasticsearch::Client.new(:hosts => [get_host_port]).tap do |client|
|
24
|
+
allow(client).to receive(:verify_elasticsearch).and_return(true) # bypass client side version checking
|
25
|
+
end
|
27
26
|
end
|
28
27
|
|
29
28
|
def doc_type
|
@@ -56,11 +55,7 @@ module ESHelper
|
|
56
55
|
end
|
57
56
|
|
58
57
|
def routing_field_name
|
59
|
-
|
60
|
-
:routing
|
61
|
-
else
|
62
|
-
:_routing
|
63
|
-
end
|
58
|
+
:routing
|
64
59
|
end
|
65
60
|
|
66
61
|
def self.es_version
|
@@ -11,7 +11,7 @@
|
|
11
11
|
"transport_address" : "http://localhost:9200",
|
12
12
|
"host" : "localhost",
|
13
13
|
"ip" : "127.0.0.1",
|
14
|
-
"version" : "
|
14
|
+
"version" : "6.8.10",
|
15
15
|
"build_hash" : "19c13d0",
|
16
16
|
"roles" : [
|
17
17
|
"master"
|
@@ -29,7 +29,7 @@
|
|
29
29
|
"transport_address" : "http://localhost:9201",
|
30
30
|
"host" : "localhost",
|
31
31
|
"ip" : "127.0.0.1",
|
32
|
-
"version" : "
|
32
|
+
"version" : "6.8.10",
|
33
33
|
"build_hash" : "19c13d0",
|
34
34
|
"roles" : [
|
35
35
|
"data"
|
@@ -47,7 +47,7 @@
|
|
47
47
|
"transport_address" : "http://localhost:9202",
|
48
48
|
"host" : "localhost",
|
49
49
|
"ip" : "127.0.0.1",
|
50
|
-
"version" : "
|
50
|
+
"version" : "6.8.10",
|
51
51
|
"build_hash" : "19c13d0",
|
52
52
|
"roles" : [
|
53
53
|
"data",
|
@@ -66,7 +66,7 @@
|
|
66
66
|
"transport_address" : "http://localhost:9203",
|
67
67
|
"host" : "localhost",
|
68
68
|
"ip" : "127.0.0.1",
|
69
|
-
"version" : "
|
69
|
+
"version" : "6.8.10",
|
70
70
|
"build_hash" : "19c13d0",
|
71
71
|
"roles" : [ ],
|
72
72
|
"http" : {
|
@@ -78,4 +78,4 @@
|
|
78
78
|
}
|
79
79
|
}
|
80
80
|
}
|
81
|
-
}
|
81
|
+
}
|
@@ -8,62 +8,63 @@ RSpec::Matchers.define :a_valid_gzip_encoded_string do
|
|
8
8
|
}
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
"http_compression" => true
|
23
|
-
}
|
11
|
+
describe "indexing with http_compression turned on", :integration => true do
|
12
|
+
let(:event) { LogStash::Event.new("message" => "Hello World!", "type" => type) }
|
13
|
+
let(:index) { 10.times.collect { rand(10).to_s }.join("") }
|
14
|
+
let(:type) { ESHelper.es_version_satisfies?("< 7") ? "doc" : "_doc" }
|
15
|
+
let(:event_count) { 10000 + rand(500) }
|
16
|
+
let(:events) { event_count.times.map { event }.to_a }
|
17
|
+
let(:config) {
|
18
|
+
{
|
19
|
+
"hosts" => get_host_port,
|
20
|
+
"index" => index,
|
21
|
+
"http_compression" => true
|
24
22
|
}
|
25
|
-
|
23
|
+
}
|
24
|
+
subject { LogStash::Outputs::ElasticSearch.new(config) }
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
26
|
+
let(:es_url) { "http://#{get_host_port}" }
|
27
|
+
let(:index_url) {"#{es_url}/#{index}"}
|
28
|
+
let(:http_client_options) { {} }
|
29
|
+
let(:http_client) do
|
30
|
+
Manticore::Client.new(http_client_options)
|
31
|
+
end
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
before do
|
34
|
+
subject.register
|
35
|
+
subject.multi_receive([])
|
36
|
+
end
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
shared_examples "an indexer" do
|
39
|
+
it "ships events" do
|
40
|
+
subject.multi_receive(events)
|
42
41
|
|
43
|
-
|
42
|
+
http_client.post("#{es_url}/_refresh").call
|
44
43
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
44
|
+
response = http_client.get("#{index_url}/_count?q=*")
|
45
|
+
result = LogStash::Json.load(response.body)
|
46
|
+
cur_count = result["count"]
|
47
|
+
expect(cur_count).to eq(event_count)
|
49
48
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
expect(doc
|
55
|
-
|
49
|
+
response = http_client.get("#{index_url}/_search?q=*&size=1000")
|
50
|
+
result = LogStash::Json.load(response.body)
|
51
|
+
result["hits"]["hits"].each do |doc|
|
52
|
+
if ESHelper.es_version_satisfies?("< 8")
|
53
|
+
expect(doc["_type"]).to eq(type)
|
54
|
+
else
|
55
|
+
expect(doc).not_to include("_type")
|
56
56
|
end
|
57
|
+
expect(doc["_index"]).to eq(index)
|
57
58
|
end
|
58
59
|
end
|
60
|
+
end
|
59
61
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
it_behaves_like("an indexer")
|
62
|
+
it "sets the correct content-encoding header and body is compressed" do
|
63
|
+
expect(subject.client.pool.adapter.client).to receive(:send).
|
64
|
+
with(anything, anything, {:headers=>{"Content-Encoding"=>"gzip", "Content-Type"=>"application/json"}, :body => a_valid_gzip_encoded_string}).
|
65
|
+
and_call_original
|
66
|
+
subject.multi_receive(events)
|
68
67
|
end
|
68
|
+
|
69
|
+
it_behaves_like("an indexer")
|
69
70
|
end
|