Skip to content

Commit 5b6f11e

Browse files
authored
Add support for 'auto' setting for ilm_enabled (#838)
* Add support for 'auto' setting for ilm_enabled Adds a new confifuration option for `ilm_enabled` - `auto`. Using `auto` will enable ILM if the target Elasticsearch cluster is > 7.0, and has the ILM feature enabled and available. * Fix code review comments, log formatting Removes puts statement, fixes indentation, and fixes misleading log messages when either the default template is used, or when a template cannot be found. * Fix doc typo
1 parent 914441c commit 5b6f11e

File tree

11 files changed

+298
-242
lines changed

11 files changed

+298
-242
lines changed

docs/index.asciidoc

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -94,26 +94,20 @@ happens, the problem is logged as a warning, and the event is dropped. See
9494
<<dead-letter-queues>> for more information about processing events in the DLQ.
9595

9696
[id="plugins-{type}s-{plugin}-ilm"]
97-
==== Index Lifecycle Management (Beta)
97+
==== Index Lifecycle Management
9898

99-
beta[]
10099

101100
[NOTE]
102101
The Index Lifecycle Management feature requires plugin version `9.3.1` or higher.
103102

104103
[NOTE]
105104
This feature requires an Elasticsearch instance of 6.6.0 or higher with at least a Basic license
106105

107-
Logstash can use the {ref}/index-lifecycle-management.html[Index Lifecycle Management] to automate the management of indices over time.
106+
Logstash can use {ref}/index-lifecycle-management.html[Index Lifecycle Management] to automate the management of indices over time.
108107

109-
To configure the Elasticsearch output to use Index Lifecycle Management, set the `ilm_enabled` flag to true in the output definition:
110-
111-
[source,ruby]
112-
output {
113-
elasticsearch {
114-
ilm_enabled => true
115-
}
116-
}
108+
The use of Index Lifecycle Management is controlled by the `ilm_enabled` setting. By default, this is will
109+
automatically detect whether the Elasticsearch instance supports ILM, and will use it if it is available. `ilm_enabled`
110+
can also be set to `true` or `false` to override the automatic detection, or disable ILM.
117111

118112
This will overwrite the index settings and adjust the Logstash template to write the necessary settings for the template
119113
to support index lifecycle management, including the index policy and rollover alias to be used.
@@ -132,7 +126,6 @@ See config below for an example:
132126
[source,ruby]
133127
output {
134128
elasticsearch {
135-
ilm_enabled => true
136129
ilm_rollover_alias: "custom"
137130
ilm_pattern: "000001"
138131
ilm_policy: "custom_policy"
@@ -192,7 +185,7 @@ This plugin supports the following configuration options plus the <<plugins-{typ
192185
| <<plugins-{type}s-{plugin}-healthcheck_path>> |<<string,string>>|No
193186
| <<plugins-{type}s-{plugin}-hosts>> |<<uri,uri>>|No
194187
| <<plugins-{type}s-{plugin}-http_compression>> |<<boolean,boolean>>|No
195-
| <<plugins-{type}s-{plugin}-ilm_enabled>> |<<boolean,boolean>>|No
188+
| <<plugins-{type}s-{plugin}-ilm_enabled>> |<<string,string>>, one of `["true", "false", "auto"]`|No
196189
| <<plugins-{type}s-{plugin}-ilm_pattern>> |<<string,string>>|No
197190
| <<plugins-{type}s-{plugin}-ilm_policy>> |<<string,string>>|No
198191
| <<plugins-{type}s-{plugin}-ilm_rollover_alias>> |<<string,string>>|No
@@ -374,10 +367,15 @@ Enable gzip compression on requests. Note that response compression is on by def
374367
[id="plugins-{type}s-{plugin}-ilm_enabled"]
375368
===== `ilm_enabled`
376369

377-
* Value type is <<boolean,boolean>>
378-
* Default value is `false`
370+
* Value can be any of: `true`, `false`, `auto`
371+
* Default value is `auto`
372+
373+
The default setting of `auto` will automatically enable the Index Lifecycle Management feature, if the Elasticsearch
374+
cluster is running Elasticsearch version `7.0.0` or higher with the ILM feature enabled, and disable it otherwise.
379375

380-
Setting this flag to `true` will enable indices to be managed by the Index Lifecycle Management feature in Elasticsearch.
376+
Setting this flag to `false` will disable the Index Lifecycle Management feature, even if the Elasticsearch cluster supports ILM.
377+
Setting this flag to `true` will enable Index Lifecycle Management feature, if the Elasticsearch cluster supports it. This is required
378+
to enable Index Lifecycle Management on a version of Elasticsearch earlier than version `7.0.0`.
381379

382380
NOTE: This feature requires a Basic License or above to be installed on an Elasticsearch cluster version 6.6.0 or later
383381

lib/logstash/outputs/elasticsearch/common.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ def setup_after_successful_connection
4747
sleep_interval = next_sleep_interval(sleep_interval)
4848
end
4949
if successful_connection?
50-
verify_ilm_readiness if ilm_enabled?
5150
install_template
5251
setup_ilm if ilm_enabled?
5352
end
@@ -339,6 +338,10 @@ def safe_bulk(actions)
339338
end
340339
end
341340

341+
def default_index?(index)
342+
@index == LogStash::Outputs::ElasticSearch::CommonConfigs::DEFAULT_INDEX_NAME
343+
end
344+
342345
def dlq_enabled?
343346
# TODO there should be a better way to query if DLQ is enabled
344347
# See more in: https://github.com/elastic/logstash/issues/8064

lib/logstash/outputs/elasticsearch/common_configs.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module CommonConfigs
55

66
DEFAULT_INDEX_NAME = "logstash-%{+YYYY.MM.dd}"
77
DEFAULT_POLICY = "logstash-policy"
8+
DEFAULT_ROLLOVER_ALIAS = 'logstash'
89

910
def self.included(mod)
1011
# The index to write events to. This can be dynamic using the `%{foo}` syntax.
@@ -141,10 +142,10 @@ def self.included(mod)
141142
# ILM configurations (beta)
142143
# -----
143144
# Flag for enabling Index Lifecycle Management integration.
144-
mod.config :ilm_enabled, :validate => :boolean, :default => false
145+
mod.config :ilm_enabled, :validate => [true, false, 'true', 'false', 'auto'], :default => 'auto'
145146

146147
# Rollover alias used for indexing data. If rollover alias doesn't exist, Logstash will create it and map it to the relevant index
147-
mod.config :ilm_rollover_alias, :validate => :string, :default => 'logstash'
148+
mod.config :ilm_rollover_alias, :validate => :string, :default => DEFAULT_ROLLOVER_ALIAS
148149

149150
# appends “{now/d}-000001” by default for new index creation, subsequent rollover indices will increment based on this pattern i.e. “000002”
150151
# {now/d} is date math, and will insert the appropriate value automatically.

lib/logstash/outputs/elasticsearch/ilm.rb

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,66 @@ module Ilm
55

66
def setup_ilm
77
return unless ilm_enabled?
8-
@logger.info("Using Index lifecycle management - this feature is currently in beta.")
9-
@logger.warn "Overwriting supplied index name with rollover alias #{@ilm_rollover_alias}" if @index != LogStash::Outputs::ElasticSearch::CommonConfigs::DEFAULT_INDEX_NAME
10-
@index = ilm_rollover_alias
8+
if default_index?(@index) || !default_rollover_alias?(@ilm_rollover_alias)
9+
logger.warn("Overwriting supplied index #{@index} with rollover alias #{@ilm_rollover_alias}") unless default_index?(@index)
10+
@index = @ilm_rollover_alias
11+
maybe_create_rollover_alias
12+
maybe_create_ilm_policy
13+
end
14+
end
1115

12-
maybe_create_rollover_alias
13-
maybe_create_ilm_policy
16+
def default_rollover_alias?(rollover_alias)
17+
rollover_alias == LogStash::Outputs::ElasticSearch::DEFAULT_ROLLOVER_ALIAS
1418
end
1519

1620
def ilm_enabled?
17-
@ilm_enabled
21+
return @ilm_actually_enabled if defined?(@ilm_actually_enabled)
22+
@ilm_actually_enabled =
23+
begin
24+
if @ilm_enabled == 'auto'
25+
if ilm_on_by_default?
26+
ilm_ready, error = ilm_ready?
27+
if !ilm_ready
28+
@logger.info("Index Lifecycle Management is set to 'auto', but will be disabled - #{error}")
29+
false
30+
else
31+
true
32+
end
33+
else
34+
@logger.info("Index Lifecycle Management is set to 'auto', but will be disabled - Your Elasticsearch cluster is before 7.0.0, which is the minimum version required to automatically run Index Lifecycle Management")
35+
false
36+
end
37+
elsif @ilm_enabled.to_s == 'true'
38+
ilm_ready, error = ilm_ready?
39+
raise LogStash::ConfigurationError,"Index Lifecycle Management is set to enabled in Logstash, but cannot be used - #{error}" unless ilm_ready
40+
true
41+
else
42+
false
43+
end
44+
end
1845
end
1946

20-
def verify_ilm_readiness
21-
return unless ilm_enabled?
47+
def ilm_on_by_default?
48+
maximum_seen_major_version >= 7
49+
end
2250

51+
def ilm_ready?
2352
# Check the Elasticsearch instance for ILM readiness - this means that the version has to be a non-OSS release, with ILM feature
2453
# available and enabled.
2554
begin
2655
xpack = client.get_xpack_info
27-
features = xpack["features"]
56+
features = xpack.nil? || xpack.empty? ? nil : xpack["features"]
2857
ilm = features.nil? ? nil : features["ilm"]
29-
raise LogStash::ConfigurationError, "Index Lifecycle management is enabled in logstash, but not installed on your Elasticsearch cluster" if features.nil? || ilm.nil?
30-
raise LogStash::ConfigurationError, "Index Lifecycle management is enabled in logstash, but not available in your Elasticsearch cluster" unless ilm['available']
31-
raise LogStash::ConfigurationError, "Index Lifecycle management is enabled in logstash, but not enabled in your Elasticsearch cluster" unless ilm['enabled']
32-
33-
unless ilm_policy_default? || client.ilm_policy_exists?(ilm_policy)
34-
raise LogStash::ConfigurationError, "The specified ILM policy #{ilm_policy} does not exist on your Elasticsearch instance"
35-
end
36-
58+
return false, "Index Lifecycle management is not installed on your Elasticsearch cluster" if features.nil? || ilm.nil?
59+
return false, "Index Lifecycle management is not available in your Elasticsearch cluster" unless ilm['available']
60+
return false, "Index Lifecycle management is not enabled in your Elasticsearch cluster" unless ilm['enabled']
61+
return true, nil
3762
rescue ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError => e
3863
# Check xpack endpoint: If no xpack endpoint, then this version of Elasticsearch is not compatible
3964
if e.response_code == 404
40-
raise LogStash::ConfigurationError, "Index Lifecycle management is enabled in logstash, but not installed on your Elasticsearch cluster"
65+
return false, "Index Lifecycle management is not installed on your Elasticsearch cluster"
4166
elsif e.response_code == 400
42-
raise LogStash::ConfigurationError, "Index Lifecycle management is enabled in logstash, but not installed on your Elasticsearch cluster"
67+
return false, "Index Lifecycle management is not installed on your Elasticsearch cluster"
4368
else
4469
raise e
4570
end
@@ -53,8 +78,10 @@ def ilm_policy_default?
5378
end
5479

5580
def maybe_create_ilm_policy
56-
if ilm_policy_default? && !client.ilm_policy_exists?(ilm_policy)
57-
client.ilm_policy_put(ilm_policy, policy_payload)
81+
if ilm_policy_default?
82+
client.ilm_policy_put(ilm_policy, policy_payload) unless client.ilm_policy_exists?(ilm_policy)
83+
else
84+
raise LogStash::ConfigurationError, "The specified ILM policy #{ilm_policy} does not exist on your Elasticsearch instance" unless client.ilm_policy_exists?(ilm_policy)
5885
end
5986
end
6087

lib/logstash/outputs/elasticsearch/template_manager.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ class TemplateManager
33
# To be mixed into the elasticsearch plugin base
44
def self.install_template(plugin)
55
return unless plugin.manage_template
6-
plugin.logger.info("Using mapping template from", :path => plugin.template)
6+
if plugin.template.nil?
7+
plugin.logger.info("Using default mapping template")
8+
else
9+
plugin.logger.info("Using mapping template from", :path => plugin.template)
10+
end
11+
12+
713
template = get_template(plugin.template, plugin.maximum_seen_major_version)
814
add_ilm_settings_to_template(plugin, template) if plugin.ilm_enabled?
915
plugin.logger.info("Attempting to install template", :manage_template => template)
@@ -23,7 +29,6 @@ def self.install(client, template_name, template, template_overwrite)
2329
end
2430

2531
def self.add_ilm_settings_to_template(plugin, template)
26-
plugin.logger.info("Overwriting index patterns, as ILM is enabled.")
2732
# Overwrite any index patterns, and use the rollover alias. Use 'index_patterns' rather than 'template' for pattern
2833
# definition - remove any existing definition of 'template'
2934
template.delete('template') if template.include?('template')
@@ -48,7 +53,7 @@ def self.default_template_path(es_major_version)
4853
end
4954

5055
def self.read_template_file(template_path)
51-
raise ArgumentError, "Template file '#{@template_path}' could not be found!" unless ::File.exists?(template_path)
56+
raise ArgumentError, "Template file '#{template_path}' could not be found!" unless ::File.exists?(template_path)
5257
template_data = ::IO.read(template_path)
5358
LogStash::Json.load(template_data)
5459
end

spec/es_spec_helper.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ def self.es_version
6767
end
6868
end
6969

70+
RSpec::Matchers.define :have_index_pattern do |expected|
71+
match do |actual|
72+
test_against = Array(actual['index_patterns'].nil? ? actual['template'] : actual['index_patterns'])
73+
test_against.include?(expected)
74+
end
75+
end
76+
7077

7178
def self.es_version_satisfies?(*requirement)
7279
es_version = RSpec.configuration.filter[:es_version] || ENV['ES_VERSION']
@@ -125,6 +132,38 @@ def supports_ilm?(client)
125132
false
126133
end
127134
end
135+
136+
def max_docs_policy(max_docs)
137+
{
138+
"policy" => {
139+
"phases"=> {
140+
"hot" => {
141+
"actions" => {
142+
"rollover" => {
143+
"max_docs" => max_docs
144+
}
145+
}
146+
}
147+
}
148+
}
149+
}
150+
end
151+
152+
def max_age_policy(max_age)
153+
{
154+
"policy" => {
155+
"phases"=> {
156+
"hot" => {
157+
"actions" => {
158+
"rollover" => {
159+
"max_age" => max_age
160+
}
161+
}
162+
}
163+
}
164+
}
165+
}
166+
end
128167
end
129168

130169
RSpec.configure do |config|

spec/integration/outputs/compressed_indexing_spec.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,11 @@
5858
end
5959

6060
it "sets the correct content-encoding header and body is compressed" do
61-
allow(subject.client.pool.adapter.client).to receive(:send).with(anything, /_template/, anything).and_call_original
61+
# Allow xpack endpoint to be checked\
62+
allow(subject.client.pool.adapter.client).to receive(:send).at_least(:once).with(anything, /_template/, anything).and_call_original
63+
allow(subject.client.pool.adapter.client).to receive(:send).with(anything, /_xpack/, anything).and_call_original
64+
allow(subject.client.pool.adapter.client).to receive(:send).with(anything, /logstash/, anything).and_call_original
65+
allow(subject.client.pool.adapter.client).to receive(:send).with(anything, /_ilm/, anything).and_call_original
6266
expect(subject.client.pool.adapter.client).to receive(:send).
6367
with(anything, anything, {:headers=>{"Content-Encoding"=>"gzip", "Content-Type"=>"application/json"}, :body => a_valid_gzip_encoded_string}).
6468
and_call_original

0 commit comments

Comments
 (0)