logstash-output-elasticsearch 10.8.6-java → 11.0.0-java
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/docs/index.asciidoc +132 -22
- data/lib/logstash/outputs/elasticsearch.rb +122 -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 +47 -34
- data/lib/logstash/outputs/elasticsearch/ilm.rb +11 -12
- data/lib/logstash/outputs/elasticsearch/license_checker.rb +19 -22
- data/lib/logstash/outputs/elasticsearch/template_manager.rb +3 -5
- data/lib/logstash/plugin_mixins/elasticsearch/api_configs.rb +157 -153
- data/lib/logstash/plugin_mixins/elasticsearch/common.rb +70 -58
- data/logstash-output-elasticsearch.gemspec +2 -2
- data/spec/es_spec_helper.rb +3 -6
- data/spec/integration/outputs/data_stream_spec.rb +61 -0
- data/spec/integration/outputs/ilm_spec.rb +6 -2
- data/spec/integration/outputs/ingest_pipeline_spec.rb +4 -2
- data/spec/integration/outputs/retry_spec.rb +4 -4
- data/spec/integration/outputs/sniffer_spec.rb +0 -1
- data/spec/spec_helper.rb +14 -0
- data/spec/unit/outputs/elasticsearch/data_stream_support_spec.rb +542 -0
- data/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb +1 -0
- data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +24 -10
- data/spec/unit/outputs/elasticsearch/http_client_spec.rb +2 -3
- data/spec/unit/outputs/elasticsearch/template_manager_spec.rb +1 -3
- data/spec/unit/outputs/elasticsearch_proxy_spec.rb +1 -2
- data/spec/unit/outputs/elasticsearch_spec.rb +122 -23
- 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 +23 -16
@@ -0,0 +1,233 @@
|
|
1
|
+
module LogStash module Outputs class ElasticSearch
|
2
|
+
# DS specific behavior/configuration.
|
3
|
+
module DataStreamSupport
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
# Defines whether data will be indexed into an Elasticsearch data stream,
|
7
|
+
# `data_stream_*` settings will only be used if this setting is enabled!
|
8
|
+
# This setting supports values `true`, `false`, and `auto`.
|
9
|
+
# Defaults to `false` in Logstash 7.x and `auto` starting in Logstash 8.0.
|
10
|
+
base.config :data_stream, :validate => ['true', 'false', 'auto']
|
11
|
+
|
12
|
+
base.config :data_stream_type, :validate => ['logs', 'metrics', 'synthetics'], :default => 'logs'
|
13
|
+
base.config :data_stream_dataset, :validate => :dataset_identifier, :default => 'generic'
|
14
|
+
base.config :data_stream_namespace, :validate => :namespace_identifier, :default => 'default'
|
15
|
+
|
16
|
+
base.config :data_stream_sync_fields, :validate => :boolean, :default => true
|
17
|
+
base.config :data_stream_auto_routing, :validate => :boolean, :default => true
|
18
|
+
|
19
|
+
base.extend(Validator)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @note assumes to be running AFTER {after_successful_connection} completed, due ES version checks
|
23
|
+
def data_stream_config?
|
24
|
+
@data_stream_config.nil? ? @data_stream_config = check_data_stream_config! : @data_stream_config
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def data_stream_name(event)
|
30
|
+
data_stream = event.get('data_stream')
|
31
|
+
return @index if !data_stream_auto_routing || !data_stream.is_a?(Hash)
|
32
|
+
|
33
|
+
type = data_stream['type'] || data_stream_type
|
34
|
+
dataset = data_stream['dataset'] || data_stream_dataset
|
35
|
+
namespace = data_stream['namespace'] || data_stream_namespace
|
36
|
+
"#{type}-#{dataset}-#{namespace}"
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param params the user configuration for the ES output
|
40
|
+
# @note LS initialized configuration (with filled defaults) won't detect as data-stream
|
41
|
+
# compatible, only explicit (`original_params`) config should be tested.
|
42
|
+
# @return [TrueClass|FalseClass] whether given configuration is data-stream compatible
|
43
|
+
def check_data_stream_config!(params = original_params)
|
44
|
+
data_stream_params = params.select { |name, _| name.start_with?('data_stream_') } # exclude data_stream =>
|
45
|
+
invalid_data_stream_params = invalid_data_stream_params(params)
|
46
|
+
|
47
|
+
case data_stream_explicit_value
|
48
|
+
when false
|
49
|
+
if data_stream_params.any?
|
50
|
+
@logger.error "Ambiguous configuration; data stream settings must not be present when data streams is disabled (caused by: `data_stream => false`)", data_stream_params
|
51
|
+
raise LogStash::ConfigurationError, "Ambiguous configuration, please remove data stream specific settings: #{data_stream_params.keys}"
|
52
|
+
end
|
53
|
+
return false
|
54
|
+
when true
|
55
|
+
if invalid_data_stream_params.any?
|
56
|
+
@logger.error "Invalid data stream configuration, following parameters are not supported:", invalid_data_stream_params
|
57
|
+
raise LogStash::ConfigurationError, "Invalid data stream configuration: #{invalid_data_stream_params.keys}"
|
58
|
+
end
|
59
|
+
return true
|
60
|
+
else
|
61
|
+
use_data_stream = data_stream_default(data_stream_params, invalid_data_stream_params.empty?)
|
62
|
+
if !use_data_stream && data_stream_params.any?
|
63
|
+
# DS (auto) disabled but there's still some data-stream parameters (and no `data_stream => false`)
|
64
|
+
@logger.error "Ambiguous configuration; data stream settings are present, but data streams are not enabled", data_stream_params
|
65
|
+
raise LogStash::ConfigurationError, "Ambiguous configuration, please set data_stream => true " +
|
66
|
+
"or remove data stream specific settings: #{data_stream_params.keys}"
|
67
|
+
end
|
68
|
+
use_data_stream
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def data_stream_explicit_value
|
73
|
+
case @data_stream
|
74
|
+
when 'true'
|
75
|
+
return true
|
76
|
+
when 'false'
|
77
|
+
return false
|
78
|
+
else
|
79
|
+
return nil # 'auto' or not set by user
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def invalid_data_stream_params(params)
|
84
|
+
shared_params = LogStash::PluginMixins::ElasticSearch::APIConfigs::CONFIG_PARAMS.keys.map(&:to_s)
|
85
|
+
params.reject do |name, value|
|
86
|
+
# NOTE: intentionally do not support explicit DS configuration like:
|
87
|
+
# - `index => ...` identifier provided by data_stream_xxx settings
|
88
|
+
# - `manage_template => false` implied by not setting the parameter
|
89
|
+
case name
|
90
|
+
when 'action'
|
91
|
+
value == 'create'
|
92
|
+
when 'routing', 'pipeline'
|
93
|
+
true
|
94
|
+
when 'data_stream'
|
95
|
+
value.to_s == 'true'
|
96
|
+
else
|
97
|
+
name.start_with?('data_stream_') ||
|
98
|
+
shared_params.include?(name) ||
|
99
|
+
inherited_internal_config_param?(name) # 'id', 'enabled_metric' etc
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def inherited_internal_config_param?(name)
|
105
|
+
self.class.superclass.get_config.key?(name.to_s) # superclass -> LogStash::Outputs::Base
|
106
|
+
end
|
107
|
+
|
108
|
+
DATA_STREAMS_ORIGIN_ES_VERSION = '7.9.0'
|
109
|
+
|
110
|
+
# @return [Gem::Version] if ES supports DS nil (or raise) otherwise
|
111
|
+
def assert_es_version_supports_data_streams
|
112
|
+
fail 'no last_es_version' unless last_es_version # assert - should not happen
|
113
|
+
es_version = Gem::Version.create(last_es_version)
|
114
|
+
if es_version < Gem::Version.create(DATA_STREAMS_ORIGIN_ES_VERSION)
|
115
|
+
@logger.error "Elasticsearch version does not support data streams, Logstash might end up writing to an index", es_version: es_version.version
|
116
|
+
# NOTE: when switching to synchronous check from register, this should be a ConfigurationError
|
117
|
+
raise LogStash::Error, "A data_stream configuration is only supported since Elasticsearch #{DATA_STREAMS_ORIGIN_ES_VERSION} " +
|
118
|
+
"(detected version #{es_version.version}), please upgrade your cluster"
|
119
|
+
end
|
120
|
+
es_version # return truthy
|
121
|
+
end
|
122
|
+
|
123
|
+
DATA_STREAMS_ENABLED_BY_DEFAULT_LS_VERSION = '8.0.0'
|
124
|
+
|
125
|
+
# when data_stream => is either 'auto' or not set
|
126
|
+
def data_stream_default(data_stream_params, valid_data_stream_config)
|
127
|
+
ds_default = Gem::Version.create(LOGSTASH_VERSION) >= Gem::Version.create(DATA_STREAMS_ENABLED_BY_DEFAULT_LS_VERSION)
|
128
|
+
|
129
|
+
if ds_default # LS 8.0
|
130
|
+
return false unless valid_data_stream_config
|
131
|
+
|
132
|
+
@logger.debug 'Configuration is data stream compliant'
|
133
|
+
return true
|
134
|
+
end
|
135
|
+
|
136
|
+
# LS 7.x
|
137
|
+
if valid_data_stream_config && !data_stream_params.any?
|
138
|
+
@logger.warn "Configuration is data stream compliant but due backwards compatibility Logstash 7.x will not assume " +
|
139
|
+
"writing to a data-stream, default behavior will change on Logstash 8.0 " +
|
140
|
+
"(set `data_stream => true/false` to disable this warning)"
|
141
|
+
end
|
142
|
+
false
|
143
|
+
end
|
144
|
+
|
145
|
+
# an {event_action_tuple} replacement when a data-stream configuration is detected
|
146
|
+
def data_stream_event_action_tuple(event)
|
147
|
+
event_data = event.to_hash
|
148
|
+
data_stream_event_sync(event_data) if data_stream_sync_fields
|
149
|
+
['create', common_event_params(event), event_data] # action always 'create'
|
150
|
+
end
|
151
|
+
|
152
|
+
DATA_STREAM_SYNC_FIELDS = [ 'type', 'dataset', 'namespace' ].freeze
|
153
|
+
|
154
|
+
def data_stream_event_sync(event_data)
|
155
|
+
data_stream = event_data['data_stream']
|
156
|
+
if data_stream.is_a?(Hash)
|
157
|
+
unless data_stream_auto_routing
|
158
|
+
sync_fields = DATA_STREAM_SYNC_FIELDS.select { |name| data_stream.key?(name) && data_stream[name] != send(:"data_stream_#{name}") }
|
159
|
+
if sync_fields.any? # these fields will need to be overwritten
|
160
|
+
info = sync_fields.inject({}) { |info, name| info[name] = data_stream[name]; info }
|
161
|
+
info[:event] = event_data
|
162
|
+
@logger.warn "Some data_stream fields are out of sync, these will be updated to reflect data-stream name", info
|
163
|
+
|
164
|
+
# NOTE: we work directly with event.to_hash data thus fine to mutate the 'data_stream' hash
|
165
|
+
sync_fields.each { |name| data_stream[name] = nil } # fallback to ||= bellow
|
166
|
+
end
|
167
|
+
end
|
168
|
+
else
|
169
|
+
unless data_stream.nil?
|
170
|
+
@logger.warn "Invalid 'data_stream' field type, due fields sync will overwrite", value: data_stream, event: event_data
|
171
|
+
end
|
172
|
+
event_data['data_stream'] = data_stream = Hash.new
|
173
|
+
end
|
174
|
+
|
175
|
+
data_stream['type'] ||= data_stream_type
|
176
|
+
data_stream['dataset'] ||= data_stream_dataset
|
177
|
+
data_stream['namespace'] ||= data_stream_namespace
|
178
|
+
|
179
|
+
event_data
|
180
|
+
end
|
181
|
+
|
182
|
+
module Validator
|
183
|
+
|
184
|
+
# @override {LogStash::Config::Mixin::validate_value} to handle custom validators
|
185
|
+
# @param value [Array<Object>]
|
186
|
+
# @param validator [nil,Array,Symbol]
|
187
|
+
# @return [Array(true,Object)]: if validation is a success, a tuple containing `true` and the coerced value
|
188
|
+
# @return [Array(false,String)]: if validation is a failure, a tuple containing `false` and the failure reason.
|
189
|
+
def validate_value(value, validator)
|
190
|
+
case validator
|
191
|
+
when :dataset_identifier then validate_dataset_identifier(value)
|
192
|
+
when :namespace_identifier then validate_namespace_identifier(value)
|
193
|
+
else super
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
private
|
198
|
+
|
199
|
+
def validate_dataset_identifier(value)
|
200
|
+
valid, value = validate_value(value, :string)
|
201
|
+
return false, value unless valid
|
202
|
+
|
203
|
+
validate_identifier(value)
|
204
|
+
end
|
205
|
+
|
206
|
+
def validate_namespace_identifier(value)
|
207
|
+
valid, value = validate_value(value, :string)
|
208
|
+
return false, value unless valid
|
209
|
+
|
210
|
+
validate_identifier(value)
|
211
|
+
end
|
212
|
+
|
213
|
+
def validate_identifier(value, max_size = 100)
|
214
|
+
if value.empty?
|
215
|
+
return false, "Invalid identifier - empty string"
|
216
|
+
end
|
217
|
+
if value.bytesize > max_size
|
218
|
+
return false, "Invalid identifier - too long (#{value.bytesize} bytes)"
|
219
|
+
end
|
220
|
+
# cannot include \, /, *, ?, ", <, >, |, ' ' (space char), ',', #, :
|
221
|
+
if value.match? Regexp.union(INVALID_IDENTIFIER_CHARS)
|
222
|
+
return false, "Invalid characters detected #{INVALID_IDENTIFIER_CHARS.inspect} are not allowed"
|
223
|
+
end
|
224
|
+
return true, value
|
225
|
+
end
|
226
|
+
|
227
|
+
INVALID_IDENTIFIER_CHARS = [ '\\', '/', '*', '?', '"', '<', '>', '|', ' ', ',', '#', ':' ]
|
228
|
+
private_constant :INVALID_IDENTIFIER_CHARS
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
end end end
|
@@ -1,6 +1,4 @@
|
|
1
1
|
require "logstash/outputs/elasticsearch"
|
2
|
-
require "cabin"
|
3
|
-
require "base64"
|
4
2
|
require 'logstash/outputs/elasticsearch/http_client/pool'
|
5
3
|
require 'logstash/outputs/elasticsearch/http_client/manticore_adapter'
|
6
4
|
require 'cgi'
|
@@ -80,12 +78,16 @@ module LogStash; module Outputs; class ElasticSearch;
|
|
80
78
|
|
81
79
|
def template_install(name, template, force=false)
|
82
80
|
if template_exists?(name) && !force
|
83
|
-
@logger.debug("Found existing Elasticsearch template
|
81
|
+
@logger.debug("Found existing Elasticsearch template, skipping template management", name: name)
|
84
82
|
return
|
85
83
|
end
|
86
84
|
template_put(name, template)
|
87
85
|
end
|
88
86
|
|
87
|
+
def last_es_version
|
88
|
+
@pool.last_es_version
|
89
|
+
end
|
90
|
+
|
89
91
|
def maximum_seen_major_version
|
90
92
|
@pool.maximum_seen_major_version
|
91
93
|
end
|
@@ -384,7 +386,7 @@ module LogStash; module Outputs; class ElasticSearch;
|
|
384
386
|
|
385
387
|
def template_put(name, template)
|
386
388
|
path = "#{template_endpoint}/#{name}"
|
387
|
-
logger.info("Installing
|
389
|
+
logger.info("Installing Elasticsearch template", name: name)
|
388
390
|
@pool.put(path, nil, LogStash::Json.dump(template))
|
389
391
|
end
|
390
392
|
|
@@ -401,13 +403,13 @@ module LogStash; module Outputs; class ElasticSearch;
|
|
401
403
|
|
402
404
|
# Create a new rollover alias
|
403
405
|
def rollover_alias_put(alias_name, alias_definition)
|
404
|
-
logger.info("Creating rollover alias #{alias_name}")
|
405
406
|
begin
|
406
407
|
@pool.put(CGI::escape(alias_name), nil, LogStash::Json.dump(alias_definition))
|
408
|
+
logger.info("Created rollover alias", name: alias_name)
|
407
409
|
# If the rollover alias already exists, ignore the error that comes back from Elasticsearch
|
408
410
|
rescue ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError => e
|
409
411
|
if e.response_code == 400
|
410
|
-
logger.info("Rollover
|
412
|
+
logger.info("Rollover alias already exists, skipping", name: alias_name)
|
411
413
|
return
|
412
414
|
end
|
413
415
|
raise e
|
@@ -428,7 +430,7 @@ module LogStash; module Outputs; class ElasticSearch;
|
|
428
430
|
|
429
431
|
def ilm_policy_put(name, policy)
|
430
432
|
path = "_ilm/policy/#{name}"
|
431
|
-
logger.info("Installing ILM policy #{policy}
|
433
|
+
logger.info("Installing ILM policy #{policy}", name: name)
|
432
434
|
@pool.put(path, nil, LogStash::Json.dump(policy))
|
433
435
|
end
|
434
436
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require "concurrent/atomic/atomic_reference"
|
1
2
|
require "logstash/plugin_mixins/elasticsearch/noop_license_checker"
|
2
3
|
|
3
4
|
module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
@@ -71,6 +72,8 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
71
72
|
@stopping = false
|
72
73
|
|
73
74
|
@license_checker = options[:license_checker] || LogStash::PluginMixins::ElasticSearch::NoopLicenseChecker::INSTANCE
|
75
|
+
|
76
|
+
@last_es_version = Concurrent::AtomicReference.new
|
74
77
|
end
|
75
78
|
|
76
79
|
def start
|
@@ -118,12 +121,6 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
118
121
|
@state_mutex.synchronize { @url_info }
|
119
122
|
end
|
120
123
|
|
121
|
-
def maximum_seen_major_version
|
122
|
-
@state_mutex.synchronize do
|
123
|
-
@maximum_seen_major_version
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
124
|
def urls
|
128
125
|
url_info.keys
|
129
126
|
end
|
@@ -252,11 +249,12 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
252
249
|
response = perform_request_to_url(url, :get, LICENSE_PATH)
|
253
250
|
LogStash::Json.load(response.body)
|
254
251
|
rescue => e
|
255
|
-
logger.error("Unable to get license information", url: url.sanitized.to_s,
|
252
|
+
logger.error("Unable to get license information", url: url.sanitized.to_s, exception: e.class, message: e.message)
|
256
253
|
{}
|
257
254
|
end
|
258
255
|
|
259
256
|
def health_check_request(url)
|
257
|
+
logger.debug("Running health check to see if an ES connection is working", url: url.sanitized.to_s, path: @healthcheck_path)
|
260
258
|
perform_request_to_url(url, :head, @healthcheck_path)
|
261
259
|
end
|
262
260
|
|
@@ -264,29 +262,20 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
264
262
|
# Try to keep locking granularity low such that we don't affect IO...
|
265
263
|
@state_mutex.synchronize { @url_info.select {|url,meta| meta[:state] != :alive } }.each do |url,meta|
|
266
264
|
begin
|
267
|
-
logger.debug("Running health check to see if an Elasticsearch connection is working",
|
268
|
-
:healthcheck_url => url, :path => @healthcheck_path)
|
269
265
|
health_check_request(url)
|
270
266
|
# If no exception was raised it must have succeeded!
|
271
|
-
logger.warn("Restored connection to ES instance", :
|
267
|
+
logger.warn("Restored connection to ES instance", url: url.sanitized.to_s)
|
272
268
|
# We reconnected to this node, check its ES version
|
273
269
|
es_version = get_es_version(url)
|
274
270
|
@state_mutex.synchronize do
|
275
271
|
meta[:version] = es_version
|
276
|
-
|
277
|
-
if !@maximum_seen_major_version
|
278
|
-
@logger.info("ES Output version determined", :es_version => major)
|
279
|
-
set_new_major_version(major)
|
280
|
-
elsif major > @maximum_seen_major_version
|
281
|
-
@logger.warn("Detected a node with a higher major version than previously observed. This could be the result of an elasticsearch cluster upgrade.", :previous_major => @maximum_seen_major_version, :new_major => major, :node_url => url.sanitized.to_s)
|
282
|
-
set_new_major_version(major)
|
283
|
-
end
|
272
|
+
set_last_es_version(es_version, url)
|
284
273
|
|
285
274
|
alive = @license_checker.appropriate_license?(self, url)
|
286
275
|
meta[:state] = alive ? :alive : :dead
|
287
276
|
end
|
288
277
|
rescue HostUnreachableError, BadResponseCodeError => e
|
289
|
-
logger.warn("Attempted to resurrect connection to dead ES instance, but got an error
|
278
|
+
logger.warn("Attempted to resurrect connection to dead ES instance, but got an error", url: url.sanitized.to_s, exception: e.class, message: e.message)
|
290
279
|
end
|
291
280
|
end
|
292
281
|
end
|
@@ -355,9 +344,7 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
355
344
|
end
|
356
345
|
|
357
346
|
if state_changes[:removed].size > 0 || state_changes[:added].size > 0
|
358
|
-
|
359
|
-
logger.info("Elasticsearch pool URLs updated", :changes => state_changes)
|
360
|
-
end
|
347
|
+
logger.info? && logger.info("Elasticsearch pool URLs updated", :changes => state_changes)
|
361
348
|
end
|
362
349
|
|
363
350
|
# Run an inline healthcheck anytime URLs are updated
|
@@ -371,10 +358,6 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
371
358
|
@state_mutex.synchronize { @url_info.size }
|
372
359
|
end
|
373
360
|
|
374
|
-
def es_versions
|
375
|
-
@state_mutex.synchronize { @url_info.size }
|
376
|
-
end
|
377
|
-
|
378
361
|
def add_url(url)
|
379
362
|
@url_info[url] ||= empty_url_meta
|
380
363
|
end
|
@@ -459,22 +442,52 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
|
459
442
|
|
460
443
|
def return_connection(url)
|
461
444
|
@state_mutex.synchronize do
|
462
|
-
|
463
|
-
|
464
|
-
end
|
445
|
+
info = @url_info[url]
|
446
|
+
info[:in_use] -= 1 if info # Guard against the condition where the connection has already been deleted
|
465
447
|
end
|
466
448
|
end
|
467
449
|
|
468
450
|
def get_es_version(url)
|
469
451
|
request = perform_request_to_url(url, :get, ROOT_URI_PATH)
|
470
|
-
LogStash::Json.load(request.body)["version"]["number"]
|
452
|
+
LogStash::Json.load(request.body)["version"]["number"] # e.g. "7.10.0"
|
453
|
+
end
|
454
|
+
|
455
|
+
def last_es_version
|
456
|
+
@last_es_version.get
|
457
|
+
end
|
458
|
+
|
459
|
+
def maximum_seen_major_version
|
460
|
+
@state_mutex.synchronize { @maximum_seen_major_version }
|
461
|
+
end
|
462
|
+
|
463
|
+
private
|
464
|
+
|
465
|
+
# @private executing within @state_mutex
|
466
|
+
def set_last_es_version(version, url)
|
467
|
+
@last_es_version.set(version)
|
468
|
+
|
469
|
+
major = major_version(version)
|
470
|
+
if @maximum_seen_major_version.nil?
|
471
|
+
@logger.info("Elasticsearch version determined (#{version})", es_version: major)
|
472
|
+
set_maximum_seen_major_version(major)
|
473
|
+
elsif major > @maximum_seen_major_version
|
474
|
+
warn_on_higher_major_version(major, url)
|
475
|
+
@maximum_seen_major_version = major
|
476
|
+
end
|
471
477
|
end
|
472
478
|
|
473
|
-
def
|
474
|
-
|
475
|
-
|
476
|
-
@logger.warn("Detected a 6.x and above cluster: the `type` event field won't be used to determine the document _type", :es_version => @maximum_seen_major_version)
|
479
|
+
def set_maximum_seen_major_version(major)
|
480
|
+
if major >= 6
|
481
|
+
@logger.warn("Detected a 6.x and above cluster: the `type` event field won't be used to determine the document _type", es_version: major)
|
477
482
|
end
|
483
|
+
@maximum_seen_major_version = major
|
478
484
|
end
|
485
|
+
|
486
|
+
def warn_on_higher_major_version(major, url)
|
487
|
+
@logger.warn("Detected a node with a higher major version than previously observed, " +
|
488
|
+
"this could be the result of an Elasticsearch cluster upgrade",
|
489
|
+
previous_major: @maximum_seen_major_version, new_major: major, node_url: url.sanitized.to_s)
|
490
|
+
end
|
491
|
+
|
479
492
|
end
|
480
493
|
end; end; end; end;
|
@@ -4,21 +4,12 @@ module LogStash; module Outputs; class ElasticSearch
|
|
4
4
|
ILM_POLICY_PATH = "default-ilm-policy.json"
|
5
5
|
|
6
6
|
def setup_ilm
|
7
|
-
return unless ilm_in_use?
|
8
7
|
logger.warn("Overwriting supplied index #{@index} with rollover alias #{@ilm_rollover_alias}") unless default_index?(@index)
|
9
8
|
@index = @ilm_rollover_alias
|
10
9
|
maybe_create_rollover_alias
|
11
10
|
maybe_create_ilm_policy
|
12
11
|
end
|
13
12
|
|
14
|
-
def default_rollover_alias?(rollover_alias)
|
15
|
-
rollover_alias == default_ilm_rollover_alias
|
16
|
-
end
|
17
|
-
|
18
|
-
def ilm_alias_set?
|
19
|
-
default_index?(@index) || !default_rollover_alias?(@ilm_rollover_alias)
|
20
|
-
end
|
21
|
-
|
22
13
|
def ilm_in_use?
|
23
14
|
return @ilm_actually_enabled if defined?(@ilm_actually_enabled)
|
24
15
|
@ilm_actually_enabled =
|
@@ -46,6 +37,12 @@ module LogStash; module Outputs; class ElasticSearch
|
|
46
37
|
end
|
47
38
|
end
|
48
39
|
|
40
|
+
private
|
41
|
+
|
42
|
+
def ilm_alias_set?
|
43
|
+
default_index?(@index) || !default_rollover_alias?(@ilm_rollover_alias)
|
44
|
+
end
|
45
|
+
|
49
46
|
def ilm_on_by_default?
|
50
47
|
maximum_seen_major_version >= 7
|
51
48
|
end
|
@@ -73,19 +70,21 @@ module LogStash; module Outputs; class ElasticSearch
|
|
73
70
|
end
|
74
71
|
end
|
75
72
|
|
76
|
-
private
|
77
|
-
|
78
73
|
def default_index?(index)
|
79
74
|
index == @default_index
|
80
75
|
end
|
81
76
|
|
77
|
+
def default_rollover_alias?(rollover_alias)
|
78
|
+
rollover_alias == default_ilm_rollover_alias
|
79
|
+
end
|
80
|
+
|
82
81
|
def ilm_policy_default?
|
83
82
|
ilm_policy == LogStash::Outputs::ElasticSearch::DEFAULT_POLICY
|
84
83
|
end
|
85
84
|
|
86
85
|
def maybe_create_ilm_policy
|
87
86
|
if ilm_policy_default?
|
88
|
-
|
87
|
+
client.ilm_policy_put(ilm_policy, policy_payload) unless client.ilm_policy_exists?(ilm_policy)
|
89
88
|
else
|
90
89
|
raise LogStash::ConfigurationError, "The specified ILM policy #{ilm_policy} does not exist on your Elasticsearch instance" unless client.ilm_policy_exists?(ilm_policy)
|
91
90
|
end
|