Skip to content

Commit 853871b

Browse files
authored
Implement Composable Templates to support Elasticsearch 8.0 (logstash-plugins#980)
Elasticsearch's /_template API is being deprecated in 7.8 Use the new endpoint /_index_template to manage template if elasticsearch version >= 8 Add a new template 8x with priority set for default usage Fixed: logstash-plugins#944
1 parent 5470c13 commit 853871b

File tree

11 files changed

+227
-76
lines changed

11 files changed

+227
-76
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 10.7.3
2+
- Added composable index template support for elasticsearch version 8 [#980](https://github.com/logstash-plugins/logstash-output-elasticsearch/pull/980)
3+
14
## 10.7.2
25
- [DOC] Fixed links to restructured Logstash-to-cloud docs [#975](https://github.com/logstash-plugins/logstash-output-elasticsearch/pull/975)
36

lib/logstash/outputs/elasticsearch/http_client.rb

+6-2
Original file line numberDiff line numberDiff line change
@@ -343,15 +343,19 @@ def exists?(path, use_get=false)
343343
end
344344

345345
def template_exists?(name)
346-
exists?("/_template/#{name}")
346+
exists?("/#{template_endpoint}/#{name}")
347347
end
348348

349349
def template_put(name, template)
350-
path = "_template/#{name}"
350+
path = "#{template_endpoint}/#{name}"
351351
logger.info("Installing elasticsearch template to #{path}")
352352
@pool.put(path, nil, LogStash::Json.dump(template))
353353
end
354354

355+
def template_endpoint
356+
maximum_seen_major_version < 8 ? '_template' : '_index_template'
357+
end
358+
355359
# ILM methods
356360

357361
# check whether rollover alias already exists

lib/logstash/outputs/elasticsearch/template_manager.rb

+8-3
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,17 @@ def self.install(client, template_name, template, template_overwrite)
3434
def self.add_ilm_settings_to_template(plugin, template)
3535
# Overwrite any index patterns, and use the rollover alias. Use 'index_patterns' rather than 'template' for pattern
3636
# definition - remove any existing definition of 'template'
37-
template.delete('template') if template.include?('template')
37+
template.delete('template') if template.include?('template') if plugin.maximum_seen_major_version < 8
3838
template['index_patterns'] = "#{plugin.ilm_rollover_alias}-*"
39-
if template['settings'] && (template['settings']['index.lifecycle.name'] || template['settings']['index.lifecycle.rollover_alias'])
39+
settings = template_settings(plugin, template)
40+
if settings && (settings['index.lifecycle.name'] || settings['index.lifecycle.rollover_alias'])
4041
plugin.logger.info("Overwriting index lifecycle name and rollover alias as ILM is enabled.")
4142
end
42-
template['settings'].update({ 'index.lifecycle.name' => plugin.ilm_policy, 'index.lifecycle.rollover_alias' => plugin.ilm_rollover_alias})
43+
settings.update({ 'index.lifecycle.name' => plugin.ilm_policy, 'index.lifecycle.rollover_alias' => plugin.ilm_rollover_alias})
44+
end
45+
46+
def self.template_settings(plugin, template)
47+
plugin.maximum_seen_major_version < 8 ? template['settings']: template['template']['settings']
4348
end
4449

4550
# Template name - if template_name set, use it
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,50 @@
11
{
22
"index_patterns" : "logstash-*",
33
"version" : 80001,
4-
"settings" : {
5-
"index.refresh_interval" : "5s",
6-
"number_of_shards": 1
7-
},
8-
"mappings" : {
9-
"dynamic_templates" : [ {
10-
"message_field" : {
11-
"path_match" : "message",
12-
"match_mapping_type" : "string",
13-
"mapping" : {
14-
"type" : "text",
15-
"norms" : false
4+
"template" : {
5+
"settings" : {
6+
"index.refresh_interval" : "5s",
7+
"number_of_shards": 1
8+
},
9+
"mappings" : {
10+
"dynamic_templates" : [ {
11+
"message_field" : {
12+
"path_match" : "message",
13+
"match_mapping_type" : "string",
14+
"mapping" : {
15+
"type" : "text",
16+
"norms" : false
17+
}
1618
}
17-
}
18-
}, {
19-
"string_fields" : {
20-
"match" : "*",
21-
"match_mapping_type" : "string",
22-
"mapping" : {
23-
"type" : "text", "norms" : false,
24-
"fields" : {
25-
"keyword" : { "type": "keyword", "ignore_above": 256 }
19+
}, {
20+
"string_fields" : {
21+
"match" : "*",
22+
"match_mapping_type" : "string",
23+
"mapping" : {
24+
"type" : "text", "norms" : false,
25+
"fields" : {
26+
"keyword" : { "type": "keyword", "ignore_above": 256 }
27+
}
2628
}
2729
}
28-
}
29-
} ],
30-
"properties" : {
31-
"@timestamp": { "type": "date"},
32-
"@version": { "type": "keyword"},
33-
"geoip" : {
34-
"dynamic": true,
35-
"properties" : {
36-
"ip": { "type": "ip" },
37-
"location" : { "type" : "geo_point" },
38-
"latitude" : { "type" : "half_float" },
39-
"longitude" : { "type" : "half_float" }
30+
} ],
31+
"properties" : {
32+
"@timestamp": { "type": "date" },
33+
"@version": { "type": "keyword" },
34+
"geoip" : {
35+
"dynamic": true,
36+
"properties" : {
37+
"ip": { "type": "ip" },
38+
"location" : { "type" : "geo_point" },
39+
"latitude" : { "type" : "half_float" },
40+
"longitude" : { "type" : "half_float" }
41+
}
4042
}
4143
}
4244
}
45+
},
46+
"priority": 200,
47+
"_meta" : {
48+
"description": "index template for logstash-output-elasticsearch"
4349
}
4450
}

logstash-output-elasticsearch.gemspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Gem::Specification.new do |s|
22
s.name = 'logstash-output-elasticsearch'
3-
s.version = '10.7.2'
3+
s.version = '10.7.3'
44

55
s.licenses = ['apache-2.0']
66
s.summary = "Stores logs in Elasticsearch"

spec/es_spec_helper.rb

+32-12
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,10 @@ def todays_date
4949
Time.now.strftime("%Y.%m.%d")
5050
end
5151

52-
53-
def default_mapping_from_mappings(mappings)
54-
if ESHelper.es_version_satisfies?(">=7")
55-
mappings
56-
else
57-
mappings["_default_"]
58-
end
59-
end
60-
6152
def field_properties_from_template(template_name, field)
62-
mappings = @es.indices.get_template(name: template_name)[template_name]["mappings"]
63-
mapping = default_mapping_from_mappings(mappings)
64-
mapping["properties"][field]["properties"]
53+
template = get_template(@es, template_name)
54+
mappings = get_template_mappings(template)
55+
mappings["properties"][field]["properties"]
6556
end
6657

6758
def routing_field_name
@@ -105,6 +96,7 @@ def self.es_version_satisfies?(*requirement)
10596

10697
def clean(client)
10798
client.indices.delete_template(:name => "*")
99+
client.indices.delete_index_template(:name => "logstash*") rescue nil
108100
# This can fail if there are no indexes, ignore failure.
109101
client.indices.delete(:index => "*") rescue nil
110102
clean_ilm(client) if supports_ilm?(client)
@@ -182,6 +174,34 @@ def max_age_policy(max_age)
182174
}
183175
}
184176
end
177+
178+
def get_template(client, name)
179+
if ESHelper.es_version_satisfies?(">=8")
180+
t = client.indices.get_index_template(name: name)
181+
t['index_templates'][0]['index_template']
182+
else
183+
t = client.indices.get_template(name: name)
184+
t[name]
185+
end
186+
end
187+
188+
def get_template_settings(template)
189+
if ESHelper.es_version_satisfies?(">=8")
190+
template['template']['settings']
191+
else
192+
template['settings']
193+
end
194+
end
195+
196+
def get_template_mappings(template)
197+
if ESHelper.es_version_satisfies?(">=8")
198+
template['template']['mappings']
199+
elsif ESHelper.es_version_satisfies?(">=7")
200+
template['mappings']
201+
else
202+
template['mappings']["_default_"]
203+
end
204+
end
185205
end
186206

187207
RSpec.configure do |config|
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"index_patterns" : "overwrite-*",
3+
"version" : 80001,
4+
"template" : {
5+
"settings" : {
6+
"index.refresh_interval" : "1s",
7+
"number_of_shards": 1
8+
},
9+
"mappings" : {
10+
"dynamic_templates" : [ {
11+
"message_field" : {
12+
"path_match" : "message",
13+
"match_mapping_type" : "string",
14+
"mapping" : {
15+
"type" : "text",
16+
"norms" : false
17+
}
18+
}
19+
}, {
20+
"string_fields" : {
21+
"match" : "*",
22+
"match_mapping_type" : "string",
23+
"mapping" : {
24+
"type" : "text", "norms" : false,
25+
"fields" : {
26+
"keyword" : { "type": "keyword", "ignore_above": 256 }
27+
}
28+
}
29+
}
30+
} ],
31+
"properties" : {
32+
"@timestamp": { "type": "date" },
33+
"@version": { "type": "keyword" },
34+
"geoip" : {
35+
"dynamic": true,
36+
"properties" : {
37+
"ip": { "type": "ip" },
38+
"location" : { "type" : "geo_point" },
39+
"latitude" : { "type" : "half_float" },
40+
"longitude" : { "type" : "half_float" }
41+
}
42+
}
43+
}
44+
}
45+
},
46+
"priority": 200,
47+
"_meta" : {
48+
"description": "index template for logstash-output-elasticsearch"
49+
}
50+
}

spec/integration/outputs/ilm_spec.rb

+34-20
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
let (:settings) { super.merge("ilm_policy" => ilm_policy_name)}
99

1010
it 'should rollover when the policy max docs is reached' do
11-
put_policy(@es,ilm_policy_name, policy)
11+
put_policy(@es, ilm_policy_name, policy)
1212
subject.register
1313

1414
subject.multi_receive([
@@ -108,9 +108,11 @@
108108
it 'should not write the ILM settings into the template' do
109109
subject.register
110110
sleep(1)
111-
expect(@es.indices.get_template(name: "logstash")["logstash"]).to have_index_pattern("logstash-*")
111+
112+
template = get_template(@es, "logstash")
113+
expect(template).to have_index_pattern("logstash-*")
112114
if ESHelper.es_version_satisfies?(">= 2")
113-
expect(@es.indices.get_template(name: "logstash")["logstash"]["settings"]['index']['lifecycle']).to be_nil
115+
expect(get_template_settings(template)['index']['lifecycle']).to be_nil
114116
end
115117
end
116118

@@ -152,16 +154,17 @@
152154
end
153155

154156
context 'with a custom template name' do
155-
let (:template_name) { "custom_template_name" }
157+
let (:template_name) { "logstash_custom_template_name" }
156158
let (:settings) { super.merge('template_name' => template_name)}
157159

158160
it 'should not write the ILM settings into the template' do
159161
subject.register
160162
sleep(1)
161163

162-
expect(@es.indices.get_template(name: template_name)[template_name]).to have_index_pattern("logstash-*")
164+
template = get_template(@es, template_name)
165+
expect(template).to have_index_pattern("logstash-*")
163166
if ESHelper.es_version_satisfies?(">= 2")
164-
expect(@es.indices.get_template(name: template_name)[template_name]["settings"]['index']['lifecycle']).to be_nil
167+
expect(get_template_settings(template)['index']['lifecycle']).to be_nil
165168
end
166169
end
167170
end
@@ -387,16 +390,20 @@
387390
it 'should write the ILM settings into the template' do
388391
subject.register
389392
sleep(1)
390-
expect(@es.indices.get_template(name: "logstash")["logstash"]).to have_index_pattern("logstash-*")
391-
expect(@es.indices.get_template(name: "logstash")["logstash"]["settings"]['index']['lifecycle']['name']).to eq("logstash-policy")
392-
expect(@es.indices.get_template(name: "logstash")["logstash"]["settings"]['index']['lifecycle']['rollover_alias']).to eq("logstash")
393+
394+
template = get_template(@es, "logstash")
395+
expect(template).to have_index_pattern("logstash-*")
396+
expect(get_template_settings(template)['index']['lifecycle']['name']).to eq("logstash-policy")
397+
expect(get_template_settings(template)['index']['lifecycle']['rollover_alias']).to eq("logstash")
393398
end
394399

395400
it_behaves_like 'an ILM enabled Logstash'
396401
end
397402

398403
context 'with a set index and a custom index pattern' do
399-
if ESHelper.es_version_satisfies?(">= 7.0")
404+
if ESHelper.es_version_satisfies?(">= 8.0")
405+
let (:template) { "spec/fixtures/template-with-policy-es8x.json" }
406+
elsif ESHelper.es_version_satisfies?(">= 7.0")
400407
let (:template) { "spec/fixtures/template-with-policy-es7x.json" }
401408
else
402409
let (:template) { "spec/fixtures/template-with-policy-es6x.json" }
@@ -408,21 +415,25 @@
408415
it 'should not overwrite the index patterns' do
409416
subject.register
410417
sleep(1)
411-
expect(@es.indices.get_template(name: "logstash")["logstash"]).to have_index_pattern("overwrite-*")
418+
419+
template = get_template(@es, "logstash")
420+
expect(template).to have_index_pattern("overwrite-*")
412421
end
413422
end
414423

415424

416425
context 'with a custom template' do
417-
let (:ilm_rollover_alias) { "the_cat_in_the_hat" }
426+
let (:ilm_rollover_alias) { "logstash_the_cat_in_the_hat" }
418427
let (:index) { ilm_rollover_alias }
419428
let(:expected_index) { index }
420429
let (:settings) { super.merge("ilm_policy" => ilm_policy_name,
421430
"template" => template,
422431
"ilm_rollover_alias" => ilm_rollover_alias)}
423432

424433

425-
if ESHelper.es_version_satisfies?(">= 7.0")
434+
if ESHelper.es_version_satisfies?(">= 8.0")
435+
let (:template) { "spec/fixtures/template-with-policy-es8x.json" }
436+
elsif ESHelper.es_version_satisfies?(">= 7.0")
426437
let (:template) { "spec/fixtures/template-with-policy-es7x.json" }
427438
else
428439
let (:template) { "spec/fixtures/template-with-policy-es6x.json" }
@@ -460,23 +471,26 @@
460471
it 'should write the ILM settings into the template' do
461472
subject.register
462473
sleep(1)
463-
expect(@es.indices.get_template(name: ilm_rollover_alias)[ilm_rollover_alias]).to have_index_pattern("#{ilm_rollover_alias}-*")
464-
expect(@es.indices.get_template(name: ilm_rollover_alias)[ilm_rollover_alias]["settings"]['index']['lifecycle']['name']).to eq(ilm_policy_name)
465-
expect(@es.indices.get_template(name: ilm_rollover_alias)[ilm_rollover_alias]["settings"]['index']['lifecycle']['rollover_alias']).to eq(ilm_rollover_alias)
474+
475+
template = get_template(@es, ilm_rollover_alias)
476+
expect(template).to have_index_pattern("#{ilm_rollover_alias}-*")
477+
expect(get_template_settings(template)['index']['lifecycle']['name']).to eq(ilm_policy_name)
478+
expect(get_template_settings(template)['index']['lifecycle']['rollover_alias']).to eq(ilm_rollover_alias)
466479
end
467480

468481
context 'with a different template_name' do
469-
let (:template_name) { "custom_template_name" }
482+
let (:template_name) { "logstash_custom_template_name" }
470483
let (:settings) { super.merge('template_name' => template_name)}
471484

472485
it_behaves_like 'an ILM enabled Logstash'
473486

474487
it 'should write the ILM settings into the template' do
475488
subject.register
476489
sleep(1)
477-
expect(@es.indices.get_template(name: template_name)[template_name]).to have_index_pattern("#{ilm_rollover_alias}-*")
478-
expect(@es.indices.get_template(name: template_name)[template_name]["settings"]['index']['lifecycle']['name']).to eq(ilm_policy_name)
479-
expect(@es.indices.get_template(name: template_name)[template_name]["settings"]['index']['lifecycle']['rollover_alias']).to eq(ilm_rollover_alias)
490+
template = get_template(@es, template_name)
491+
expect(template).to have_index_pattern("#{ilm_rollover_alias}-*")
492+
expect(get_template_settings(template)['index']['lifecycle']['name']).to eq(ilm_policy_name)
493+
expect(get_template_settings(template)['index']['lifecycle']['rollover_alias']).to eq(ilm_rollover_alias)
480494
end
481495
end
482496

0 commit comments

Comments
 (0)