logstash-output-elasticsearch 9.2.4-java → 9.3.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.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/docs/index.asciidoc +104 -2
  4. data/lib/logstash/outputs/elasticsearch.rb +4 -0
  5. data/lib/logstash/outputs/elasticsearch/common.rb +8 -5
  6. data/lib/logstash/outputs/elasticsearch/common_configs.rb +23 -1
  7. data/lib/logstash/outputs/elasticsearch/default-ilm-policy.json +14 -0
  8. data/lib/logstash/outputs/elasticsearch/http_client.rb +47 -2
  9. data/lib/logstash/outputs/elasticsearch/http_client/manticore_adapter.rb +3 -3
  10. data/lib/logstash/outputs/elasticsearch/ilm.rb +84 -0
  11. data/lib/logstash/outputs/elasticsearch/template_manager.rb +21 -1
  12. data/logstash-output-elasticsearch.gemspec +1 -1
  13. data/spec/es_spec_helper.rb +70 -0
  14. data/spec/fixtures/template-with-policy-es6x.json +48 -0
  15. data/spec/fixtures/template-with-policy-es7x.json +46 -0
  16. data/spec/integration/outputs/create_spec.rb +1 -1
  17. data/spec/integration/outputs/ilm_spec.rb +542 -0
  18. data/spec/integration/outputs/ingest_pipeline_spec.rb +2 -2
  19. data/spec/integration/outputs/no_es_on_startup_spec.rb +2 -2
  20. data/spec/integration/outputs/retry_spec.rb +3 -3
  21. data/spec/integration/outputs/templates_5x_spec.rb +7 -7
  22. data/spec/integration/outputs/templates_spec.rb +7 -7
  23. data/spec/support/elasticsearch/api/actions/delete_ilm_policy.rb +19 -0
  24. data/spec/support/elasticsearch/api/actions/get_alias.rb +18 -0
  25. data/spec/support/elasticsearch/api/actions/get_ilm_policy.rb +18 -0
  26. data/spec/support/elasticsearch/api/actions/put_alias.rb +24 -0
  27. data/spec/support/elasticsearch/api/actions/put_ilm_policy.rb +25 -0
  28. metadata +20 -2
@@ -5,8 +5,9 @@ module LogStash; module Outputs; class ElasticSearch
5
5
  return unless plugin.manage_template
6
6
  plugin.logger.info("Using mapping template from", :path => plugin.template)
7
7
  template = get_template(plugin.template, plugin.maximum_seen_major_version)
8
+ add_ilm_settings_to_template(plugin, template) if plugin.ilm_enabled?
8
9
  plugin.logger.info("Attempting to install template", :manage_template => template)
9
- install(plugin.client, plugin.template_name, template, plugin.template_overwrite)
10
+ install(plugin.client, template_name(plugin), template, plugin.template_overwrite)
10
11
  rescue => e
11
12
  plugin.logger.error("Failed to install template.", :message => e.message, :class => e.class.name, :backtrace => e.backtrace)
12
13
  end
@@ -21,6 +22,25 @@ module LogStash; module Outputs; class ElasticSearch
21
22
  client.template_install(template_name, template, template_overwrite)
22
23
  end
23
24
 
25
+ def self.add_ilm_settings_to_template(plugin, template)
26
+ plugin.logger.info("Overwriting index patterns, as ILM is enabled.")
27
+ # Overwrite any index patterns, and use the rollover alias. Use 'index_patterns' rather than 'template' for pattern
28
+ # definition - remove any existing definition of 'template'
29
+ template.delete('template') if template.include?('template')
30
+ template['index_patterns'] = "#{plugin.ilm_rollover_alias}-*"
31
+ if template['settings'] && (template['settings']['index.lifecycle.name'] || template['settings']['index.lifecycle.rollover_alias'])
32
+ plugin.logger.info("Overwriting index lifecycle name and rollover alias as ILM is enabled.")
33
+ end
34
+ template['settings'].update({ 'index.lifecycle.name' => plugin.ilm_policy, 'index.lifecycle.rollover_alias' => plugin.ilm_rollover_alias})
35
+ end
36
+
37
+ # Template name - if template_name set, use it
38
+ # if not and ILM is enabled, use the rollover alias
39
+ # else use the default value of template_name
40
+ def self.template_name(plugin)
41
+ plugin.ilm_enabled? && !plugin.original_params.key?('template_name') ? plugin.ilm_rollover_alias : plugin.template_name
42
+ end
43
+
24
44
  def self.default_template_path(es_major_version)
25
45
  template_version = es_major_version == 1 ? 2 : es_major_version
26
46
  default_template_name = "elasticsearch-template-es#{template_version}x.json"
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-output-elasticsearch'
3
- s.version = '9.2.4'
3
+ s.version = '9.3.0'
4
4
  s.licenses = ['apache-2.0']
5
5
  s.summary = "Stores logs in Elasticsearch"
6
6
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -1,6 +1,13 @@
1
1
  require "logstash/devutils/rspec/spec_helper"
2
2
  require 'manticore'
3
3
  require 'elasticsearch'
4
+ require_relative "support/elasticsearch/api/actions/delete_ilm_policy"
5
+ require_relative "support/elasticsearch/api/actions/get_alias"
6
+ require_relative "support/elasticsearch/api/actions/put_alias"
7
+ require_relative "support/elasticsearch/api/actions/get_ilm_policy"
8
+ require_relative "support/elasticsearch/api/actions/put_ilm_policy"
9
+
10
+ require 'json'
4
11
 
5
12
  module ESHelper
6
13
  def get_host_port
@@ -19,6 +26,10 @@ module ESHelper
19
26
  end
20
27
  end
21
28
 
29
+ def todays_date
30
+ Time.now.strftime("%Y.%m.%d")
31
+ end
32
+
22
33
  def mapping_name
23
34
  if ESHelper.es_version_satisfies?(">=7")
24
35
  "_doc"
@@ -40,6 +51,17 @@ module ESHelper
40
51
  RSpec.configuration.filter[:es_version] || ENV['ES_VERSION']
41
52
  end
42
53
 
54
+ RSpec::Matchers.define :have_hits do |expected|
55
+ match do |actual|
56
+ if ESHelper.es_version_satisfies?(">=7")
57
+ expected == actual['hits']['total']['value']
58
+ else
59
+ expected == actual['hits']['total']
60
+ end
61
+ end
62
+ end
63
+
64
+
43
65
  def self.es_version_satisfies?(*requirement)
44
66
  es_version = RSpec.configuration.filter[:es_version] || ENV['ES_VERSION']
45
67
  if es_version.nil?
@@ -49,6 +71,54 @@ module ESHelper
49
71
  es_release_version = Gem::Version.new(es_version).release
50
72
  Gem::Requirement.new(requirement).satisfied_by?(es_release_version)
51
73
  end
74
+
75
+ def clean(client)
76
+ client.indices.delete_template(:name => "*")
77
+ # This can fail if there are no indexes, ignore failure.
78
+ client.indices.delete(:index => "*") rescue nil
79
+ clean_ilm(client) if supports_ilm?(client)
80
+ end
81
+
82
+ def set_cluster_settings(client, cluster_settings)
83
+ client.cluster.put_settings(body: cluster_settings)
84
+ get_cluster_settings(client)
85
+ end
86
+
87
+ def get_cluster_settings(client)
88
+ client.cluster.get_settings
89
+ end
90
+
91
+ def get_policy(client, policy_name)
92
+ client.get_ilm_policy(name: policy_name)
93
+ end
94
+
95
+ def put_policy(client, policy_name, policy)
96
+ client.put_ilm_policy({:name => policy_name, :body=> policy})
97
+ end
98
+
99
+ def put_alias(client, the_alias, index)
100
+ body = {
101
+ "aliases" => {
102
+ index => {
103
+ "is_write_index"=> true
104
+ }
105
+ }
106
+ }
107
+ client.put_alias({name: the_alias, body: body})
108
+ end
109
+
110
+ def clean_ilm(client)
111
+ client.get_ilm_policy.each_key {|key| client.delete_ilm_policy(name: key)}
112
+ end
113
+
114
+ def supports_ilm?(client)
115
+ begin
116
+ client.get_ilm_policy
117
+ true
118
+ rescue
119
+ false
120
+ end
121
+ end
52
122
  end
53
123
 
54
124
  RSpec.configure do |config|
@@ -0,0 +1,48 @@
1
+ {
2
+ "template" : "overwrite-*",
3
+ "version" : 60001,
4
+ "settings" : {
5
+ "index.refresh_interval" : "1s",
6
+ "number_of_shards": 1,
7
+ "index.lifecycle.name": "overwrite-policy",
8
+ "index.lifecycle.rollover_alias": "overwrite"
9
+ },
10
+ "mappings" : {
11
+ "_default_" : {
12
+ "dynamic_templates" : [ {
13
+ "message_field" : {
14
+ "path_match" : "message",
15
+ "match_mapping_type" : "string",
16
+ "mapping" : {
17
+ "type" : "text",
18
+ "norms" : false
19
+ }
20
+ }
21
+ }, {
22
+ "string_fields" : {
23
+ "match" : "*",
24
+ "match_mapping_type" : "string",
25
+ "mapping" : {
26
+ "type" : "text", "norms" : false,
27
+ "fields" : {
28
+ "keyword" : { "type": "keyword", "ignore_above": 256 }
29
+ }
30
+ }
31
+ }
32
+ } ],
33
+ "properties" : {
34
+ "@timestamp": { "type": "date"},
35
+ "@version": { "type": "keyword"},
36
+ "geoip" : {
37
+ "dynamic": true,
38
+ "properties" : {
39
+ "ip": { "type": "ip" },
40
+ "location" : { "type" : "geo_point" },
41
+ "latitude" : { "type" : "half_float" },
42
+ "longitude" : { "type" : "half_float" }
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "index_patterns" : "overwrite-*",
3
+ "version" : 60001,
4
+ "settings" : {
5
+ "index.refresh_interval" : "1s",
6
+ "number_of_shards": 1
7
+ },
8
+ "mappings" : {
9
+ "_doc" : {
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
+ }
@@ -35,7 +35,7 @@ describe "client create actions", :integration => true do
35
35
  # Wait or fail until everything's indexed.
36
36
  Stud::try(3.times) do
37
37
  r = @es.search
38
- expect(r["hits"]["total"]).to eq(1)
38
+ expect(r).to have_hits(1)
39
39
  end
40
40
  end
41
41
 
@@ -0,0 +1,542 @@
1
+ require_relative "../../../spec/es_spec_helper"
2
+
3
+ shared_examples_for 'an Elasticsearch instance that does not support index lifecycle management' do
4
+ require "logstash/outputs/elasticsearch"
5
+
6
+ let (:ilm_enabled) { false }
7
+ let (:settings) {
8
+ {
9
+ "ilm_enabled" => ilm_enabled,
10
+ "hosts" => "#{get_host_port()}"
11
+ }
12
+ }
13
+
14
+ before :each do
15
+ require "elasticsearch"
16
+
17
+ # Clean ES of data before we start.
18
+ @es = get_client
19
+ clean(@es)
20
+ end
21
+
22
+ after :each do
23
+ clean(@es)
24
+ end
25
+
26
+ subject { LogStash::Outputs::ElasticSearch.new(settings) }
27
+
28
+ context 'when ilm is enabled in Logstash' do
29
+ let (:ilm_enabled) { true }
30
+
31
+ it 'should raise a configuration error' do
32
+ expect do
33
+ begin
34
+ subject.register
35
+ sleep(1)
36
+ ensure
37
+ subject.stop_template_installer
38
+ end
39
+ end.to raise_error(LogStash::ConfigurationError)
40
+ end
41
+ end
42
+
43
+ context 'when ilm is disabled in Logstash' do
44
+ it 'should index documents normally' do
45
+ subject.register
46
+
47
+ subject.multi_receive([
48
+ LogStash::Event.new("message" => "sample message here"),
49
+ LogStash::Event.new("somemessage" => { "message" => "sample nested message here" }),
50
+ LogStash::Event.new("somevalue" => 100),
51
+ ])
52
+
53
+ sleep(6)
54
+
55
+ subject.multi_receive([
56
+ LogStash::Event.new("country" => "us"),
57
+ LogStash::Event.new("country" => "at"),
58
+ LogStash::Event.new("geoip" => { "location" => [ 0.0, 0.0 ] })
59
+ ])
60
+
61
+ @es.indices.refresh
62
+
63
+ # Wait or fail until everything's indexed.
64
+ Stud::try(20.times) do
65
+ r = @es.search
66
+ expect(r).to have_hits(6)
67
+ end
68
+ indexes_written = @es.search['hits']['hits'].each_with_object(Hash.new(0)) do |x, res|
69
+ index_written = x['_index']
70
+ res[index_written] += 1
71
+ end
72
+ expect(indexes_written.count).to eq(1)
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ shared_examples_for 'an ILM enabled Logstash' do
79
+
80
+ context 'with a policy with a maximum number of documents' do
81
+ let (:policy) { small_max_doc_policy }
82
+ let (:ilm_policy_name) { "custom-policy"}
83
+ let (:settings) { super.merge("ilm_policy" => ilm_policy_name)}
84
+
85
+ it 'should rollover when the policy max docs is reached' do
86
+ put_policy(@es,ilm_policy_name, policy)
87
+ subject.register
88
+
89
+ subject.multi_receive([
90
+ LogStash::Event.new("message" => "sample message here"),
91
+ LogStash::Event.new("somemessage" => { "message" => "sample nested message here" }),
92
+ LogStash::Event.new("somevalue" => 100),
93
+ ])
94
+
95
+ sleep(6)
96
+
97
+ subject.multi_receive([
98
+ LogStash::Event.new("country" => "us"),
99
+ LogStash::Event.new("country" => "at"),
100
+ LogStash::Event.new("geoip" => { "location" => [ 0.0, 0.0 ] })
101
+ ])
102
+
103
+ sleep(6)
104
+
105
+ subject.multi_receive([
106
+ LogStash::Event.new("country" => "uk"),
107
+ LogStash::Event.new("country" => "fr"),
108
+ LogStash::Event.new("geoip" => { "location" => [ 0.1, 1.0 ] })
109
+ ])
110
+
111
+ @es.indices.refresh
112
+
113
+ # Wait or fail until everything's indexed.
114
+ Stud::try(20.times) do
115
+ r = @es.search
116
+ expect(r).to have_hits(9)
117
+ end
118
+ indexes_written = @es.search['hits']['hits'].each_with_object(Hash.new(0)) do |x, res|
119
+ index_written = x['_index']
120
+ res[index_written] += 1
121
+ end
122
+ expect(indexes_written.count).to eq(3)
123
+ expect(indexes_written["#{expected_index}-#{todays_date}-000001"]).to eq(3)
124
+ expect(indexes_written["#{expected_index}-#{todays_date}-000002"]).to eq(3)
125
+ expect(indexes_written["#{expected_index}-#{todays_date}-000003"]).to eq(3)
126
+ end
127
+ end
128
+
129
+ context 'with a policy where the maximum number of documents is not reached' do
130
+ let (:policy) { large_max_doc_policy }
131
+ let (:ilm_policy_name) { "custom-policy"}
132
+ let (:settings) { super.merge("ilm_policy" => ilm_policy_name)}
133
+
134
+ it 'should ingest into a single index when max docs is not reached' do
135
+ put_policy(@es,ilm_policy_name, policy)
136
+ subject.register
137
+
138
+ subject.multi_receive([
139
+ LogStash::Event.new("message" => "sample message here"),
140
+ LogStash::Event.new("somemessage" => { "message" => "sample nested message here" }),
141
+ LogStash::Event.new("somevalue" => 100),
142
+ ])
143
+
144
+ sleep(6)
145
+
146
+ subject.multi_receive([
147
+ LogStash::Event.new("country" => "us"),
148
+ LogStash::Event.new("country" => "at"),
149
+ LogStash::Event.new("geoip" => { "location" => [ 0.0, 0.0 ] })
150
+ ])
151
+
152
+ @es.indices.refresh
153
+
154
+ # Wait or fail until everything's indexed.
155
+ Stud::try(20.times) do
156
+ r = @es.search
157
+ expect(r).to have_hits(6)
158
+ end
159
+ indexes_written = @es.search['hits']['hits'].each_with_object(Hash.new(0)) do |x, res|
160
+ index_written = x['_index']
161
+ res[index_written] += 1
162
+ end
163
+ expect(indexes_written.count).to eq(1)
164
+ expect(indexes_written["#{expected_index}-#{todays_date}-000001"]).to eq(6)
165
+ end
166
+ end
167
+ end
168
+
169
+
170
+ if ESHelper.es_version_satisfies?("<= 6.5")
171
+ describe 'Pre-ILM versions of Elasticsearch', :integration => true do
172
+ it_behaves_like 'an Elasticsearch instance that does not support index lifecycle management'
173
+ end
174
+ end
175
+
176
+ if ESHelper.es_version_satisfies?(">= 6.6")
177
+ describe 'OSS Elasticsearch', :distribution => 'oss', :integration => true do
178
+ it_behaves_like 'an Elasticsearch instance that does not support index lifecycle management'
179
+ end
180
+
181
+ describe 'Elasticsearch has index lifecycle management enabled', :distribution => 'xpack', :integration => true do
182
+ DEFAULT_INTERVAL = '600s'
183
+
184
+ require "logstash/outputs/elasticsearch"
185
+ let (:ilm_enabled) { true }
186
+
187
+ let (:settings) {
188
+ {
189
+ "ilm_enabled" => ilm_enabled,
190
+ "hosts" => "#{get_host_port()}"
191
+ }
192
+ }
193
+ let (:policy) { small_max_doc_policy }
194
+
195
+
196
+ let (:small_max_doc_policy) {
197
+ {"policy" => {
198
+ "phases"=> {
199
+ "hot" => {
200
+ "actions" => {
201
+ "rollover" => {
202
+ "max_docs" => "3"
203
+ }
204
+ }
205
+ }
206
+ }
207
+ }}
208
+ }
209
+
210
+ let (:large_max_doc_policy) {
211
+ {"policy" => {
212
+ "phases"=> {
213
+ "hot" => {
214
+ "actions" => {
215
+ "rollover" => {
216
+ "max_docs" => "1000000"
217
+ }
218
+ }
219
+ }
220
+ }
221
+ }}
222
+ }
223
+
224
+
225
+
226
+ subject { LogStash::Outputs::ElasticSearch.new(settings) }
227
+
228
+ before :each do
229
+ # Delete all templates first.
230
+ require "elasticsearch"
231
+
232
+ # Clean ES of data before we start.
233
+ @es = get_client
234
+ clean(@es)
235
+ # Set the poll interval for lifecycle management to be short so changes get picked up in time.
236
+ set_cluster_settings(@es, {
237
+ "persistent" => {
238
+ "indices.lifecycle.poll_interval" => "1s"
239
+ }
240
+ })
241
+ end
242
+
243
+ after :each do
244
+ # Set poll interval back to default
245
+ set_cluster_settings(@es, {
246
+ "persistent" => {
247
+ "indices.lifecycle.poll_interval" => DEFAULT_INTERVAL
248
+ }
249
+ })
250
+ clean(@es)
251
+ end
252
+
253
+
254
+ context 'with ilm enabled' do
255
+ let (:ilm_enabled) { true }
256
+
257
+
258
+ context 'when using the default policy' do
259
+ context 'with a custom pattern' do
260
+ let (:settings) { super.merge("ilm_pattern" => "000001")}
261
+ it 'should create a rollover alias' do
262
+ expect(@es.indices.exists_alias(index: "logstash")).to be_falsey
263
+ subject.register
264
+ sleep(1)
265
+ expect(@es.indices.exists_alias(index: "logstash")).to be_truthy
266
+ expect(@es.get_alias(name: "logstash")).to include("logstash-000001")
267
+ end
268
+ end
269
+
270
+
271
+ it 'should install it if it is not present' do
272
+ expect{get_policy(@es, LogStash::Outputs::ElasticSearch::DEFAULT_POLICY)}.to raise_error(Elasticsearch::Transport::Transport::Errors::NotFound)
273
+ subject.register
274
+ sleep(1)
275
+ expect{get_policy(@es, LogStash::Outputs::ElasticSearch::DEFAULT_POLICY)}.not_to raise_error
276
+ end
277
+
278
+ it 'should create the default rollover alias' do
279
+ expect(@es.indices.exists_alias(index: "logstash")).to be_falsey
280
+ subject.register
281
+ sleep(1)
282
+ expect(@es.indices.exists_alias(index: "logstash")).to be_truthy
283
+ expect(@es.get_alias(name: "logstash")).to include("logstash-#{todays_date}-000001")
284
+ end
285
+
286
+
287
+ it 'should ingest into a single index' do
288
+ subject.register
289
+
290
+ subject.multi_receive([
291
+ LogStash::Event.new("message" => "sample message here"),
292
+ LogStash::Event.new("somemessage" => { "message" => "sample nested message here" }),
293
+ LogStash::Event.new("somevalue" => 100),
294
+ ])
295
+
296
+ sleep(6)
297
+
298
+ subject.multi_receive([
299
+ LogStash::Event.new("country" => "us"),
300
+ LogStash::Event.new("country" => "at"),
301
+ LogStash::Event.new("geoip" => { "location" => [ 0.0, 0.0 ] })
302
+ ])
303
+
304
+ @es.indices.refresh
305
+
306
+ # Wait or fail until everything's indexed.
307
+ Stud::try(20.times) do
308
+ r = @es.search
309
+ expect(r).to have_hits(6)
310
+ end
311
+ indexes_written = @es.search['hits']['hits'].each_with_object(Hash.new(0)) do |x, res|
312
+ index_written = x['_index']
313
+ res[index_written] += 1
314
+ end
315
+
316
+ expect(indexes_written.count).to eq(1)
317
+ expect(indexes_written["logstash-#{todays_date}-000001"]).to eq(6)
318
+ end
319
+ end
320
+
321
+ context 'when not using the default policy' do
322
+ let (:ilm_policy_name) {"new_one"}
323
+ let (:settings) { super.merge("ilm_policy" => ilm_policy_name)}
324
+ let (:policy) {{
325
+ "policy" => {
326
+ "phases"=> {
327
+ "hot" => {
328
+ "actions" => {
329
+ "rollover" => {
330
+ "max_docs" => "3"
331
+ }
332
+ }
333
+ }
334
+ }
335
+ }}}
336
+
337
+ before do
338
+ expect{get_policy(@es, LogStash::Outputs::ElasticSearch::DEFAULT_POLICY)}.to raise_error(Elasticsearch::Transport::Transport::Errors::NotFound)
339
+ put_policy(@es,ilm_policy_name, policy)
340
+ end
341
+
342
+ it 'should not install the default policy if it is not used' do
343
+ subject.register
344
+ sleep(1)
345
+ expect{get_policy(@es, LogStash::Outputs::ElasticSearch::DEFAULT_POLICY)}.to raise_error(Elasticsearch::Transport::Transport::Errors::NotFound)
346
+ end
347
+ end
348
+
349
+ context 'when using a time based policy' do
350
+ let (:ilm_policy_name) {"new_one"}
351
+ let (:settings) { super.merge("ilm_policy" => ilm_policy_name)}
352
+ let (:policy) {{
353
+ "policy" => {
354
+ "phases"=> {
355
+ "hot" => {
356
+ "actions" => {
357
+ "rollover" => {
358
+ "max_age" => "1d"
359
+ }
360
+ }
361
+ }
362
+ }
363
+ }}}
364
+
365
+ before do
366
+ expect{get_policy(@es, LogStash::Outputs::ElasticSearch::DEFAULT_POLICY)}.to raise_error(Elasticsearch::Transport::Transport::Errors::NotFound)
367
+ put_policy(@es,ilm_policy_name, policy)
368
+ end
369
+
370
+ it 'should not install the default policy if it is not used' do
371
+ subject.register
372
+ sleep(1)
373
+ expect{get_policy(@es, LogStash::Outputs::ElasticSearch::DEFAULT_POLICY)}.to raise_error(Elasticsearch::Transport::Transport::Errors::NotFound)
374
+ end
375
+ end
376
+ context 'with the default template' do
377
+ let(:expected_index) { "logstash" }
378
+
379
+ it 'should create the rollover alias' do
380
+ expect(@es.indices.exists_alias(index: expected_index)).to be_falsey
381
+ subject.register
382
+ sleep(1)
383
+ expect(@es.indices.exists_alias(index: expected_index)).to be_truthy
384
+ expect(@es.get_alias(name: expected_index)).to include("#{expected_index}-#{todays_date}-000001")
385
+ end
386
+
387
+ it 'should write the ILM settings into the template' do
388
+ subject.register
389
+ sleep(1)
390
+ expect(@es.indices.get_template(name: "logstash")["logstash"]["index_patterns"]).to eq(["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
+ end
394
+
395
+ it_behaves_like 'an ILM enabled Logstash'
396
+ end
397
+
398
+ context 'with a custom template' do
399
+ let (:ilm_rollover_alias) { "the_cat_in_the_hat" }
400
+ let (:index) { ilm_rollover_alias }
401
+ let(:expected_index) { index }
402
+ let (:settings) { super.merge("ilm_policy" => ilm_policy_name,
403
+ "template" => template,
404
+ "ilm_rollover_alias" => ilm_rollover_alias)}
405
+
406
+
407
+ if ESHelper.es_version_satisfies?(">= 7.0")
408
+ let (:template) { "spec/fixtures/template-with-policy-es7x.json" }
409
+ else
410
+ let (:template) { "spec/fixtures/template-with-policy-es6x.json" }
411
+ end
412
+ let (:ilm_enabled) { true }
413
+ let (:ilm_policy_name) { "custom-policy" }
414
+
415
+
416
+ before :each do
417
+ put_policy(@es,ilm_policy_name, policy)
418
+ end
419
+
420
+ it_behaves_like 'an ILM enabled Logstash'
421
+
422
+ it 'should create the rollover alias' do
423
+ expect(@es.indices.exists_alias(index: ilm_rollover_alias)).to be_falsey
424
+ subject.register
425
+ sleep(1)
426
+ expect(@es.indices.exists_alias(index: ilm_rollover_alias)).to be_truthy
427
+ expect(@es.get_alias(name: ilm_rollover_alias)).to include("#{ilm_rollover_alias}-#{todays_date}-000001")
428
+ end
429
+
430
+ context 'when the custom rollover alias already exists' do
431
+ it 'should ignore the already exists error' do
432
+ expect(@es.indices.exists_alias(index: ilm_rollover_alias)).to be_falsey
433
+ put_alias(@es, "#{ilm_rollover_alias}-#{todays_date}-000001", ilm_rollover_alias)
434
+ expect(@es.indices.exists_alias(index: ilm_rollover_alias)).to be_truthy
435
+ subject.register
436
+ sleep(1)
437
+ expect(@es.get_alias(name: ilm_rollover_alias)).to include("#{ilm_rollover_alias}-#{todays_date}-000001")
438
+ end
439
+
440
+ end
441
+
442
+ it 'should write the ILM settings into the template' do
443
+ subject.register
444
+ sleep(1)
445
+ expect(@es.indices.get_template(name: ilm_rollover_alias)[ilm_rollover_alias]["index_patterns"]).to eq(["#{ilm_rollover_alias}-*"])
446
+ expect(@es.indices.get_template(name: ilm_rollover_alias)[ilm_rollover_alias]["settings"]['index']['lifecycle']['name']).to eq(ilm_policy_name)
447
+ expect(@es.indices.get_template(name: ilm_rollover_alias)[ilm_rollover_alias]["settings"]['index']['lifecycle']['rollover_alias']).to eq(ilm_rollover_alias)
448
+ end
449
+
450
+ context 'with a different template_name' do
451
+ let (:template_name) { "custom_template_name" }
452
+ let (:settings) { super.merge('template_name' => template_name)}
453
+
454
+ it_behaves_like 'an ILM enabled Logstash'
455
+
456
+ it 'should write the ILM settings into the template' do
457
+ subject.register
458
+ sleep(1)
459
+ expect(@es.indices.get_template(name: template_name)[template_name]["index_patterns"]).to eq(["#{ilm_rollover_alias}-*"])
460
+ expect(@es.indices.get_template(name: template_name)[template_name]["settings"]['index']['lifecycle']['name']).to eq(ilm_policy_name)
461
+ expect(@es.indices.get_template(name: template_name)[template_name]["settings"]['index']['lifecycle']['rollover_alias']).to eq(ilm_rollover_alias)
462
+ end
463
+ end
464
+
465
+ end
466
+ end
467
+
468
+ context 'with ilm disabled' do
469
+ let (:ilm_enabled) { false }
470
+
471
+ it 'should not create a rollover alias' do
472
+ expect(@es.get_alias).to be_empty
473
+ subject.register
474
+ sleep(1)
475
+ expect(@es.get_alias).to be_empty
476
+ end
477
+
478
+ it 'should not install the default policy' do
479
+ subject.register
480
+ sleep(1)
481
+ expect{get_policy(@es, LogStash::Outputs::ElasticSearch::DEFAULT_POLICY)}.to raise_error(Elasticsearch::Transport::Transport::Errors::NotFound)
482
+ end
483
+
484
+ it 'should not write the ILM settings into the template' do
485
+ subject.register
486
+ sleep(1)
487
+ expect(@es.indices.get_template(name: "logstash")["logstash"]["index_patterns"]).to eq(["logstash-*"])
488
+ expect(@es.indices.get_template(name: "logstash")["logstash"]["settings"]['index']['lifecycle']).to be_nil
489
+ end
490
+
491
+ context 'with an existing policy that will roll over' do
492
+ let (:policy) { small_max_doc_policy }
493
+ let (:ilm_policy_name) { "3_docs"}
494
+ let (:settings) { super.merge("ilm_policy" => ilm_policy_name)}
495
+
496
+ it 'should not roll over indices' do
497
+ subject.register
498
+ subject.multi_receive([
499
+ LogStash::Event.new("message" => "sample message here"),
500
+ LogStash::Event.new("somemessage" => { "message" => "sample nested message here" }),
501
+ LogStash::Event.new("somevalue" => 100),
502
+ ])
503
+
504
+ sleep(6)
505
+
506
+ subject.multi_receive([
507
+ LogStash::Event.new("country" => "us"),
508
+ LogStash::Event.new("country" => "at"),
509
+ LogStash::Event.new("geoip" => { "location" => [ 0.0, 0.0 ] })
510
+ ])
511
+
512
+ @es.indices.refresh
513
+
514
+ # Wait or fail until everything's indexed.
515
+ Stud::try(20.times) do
516
+ r = @es.search
517
+ expect(r).to have_hits(6)
518
+ end
519
+ indexes_written = @es.search['hits']['hits'].each_with_object(Hash.new(0)) do |x, res|
520
+ index_written = x['_index']
521
+ res[index_written] += 1
522
+ end
523
+ expect(indexes_written.count).to eq(1)
524
+ expect(indexes_written.values.first).to eq(6)
525
+ end
526
+ end
527
+
528
+ context 'with a custom template name' do
529
+ let (:template_name) { "custom_template_name" }
530
+ let (:settings) { super.merge('template_name' => template_name)}
531
+
532
+ it 'should not write the ILM settings into the template' do
533
+ subject.register
534
+ sleep(1)
535
+ expect(@es.indices.get_template(name: template_name)[template_name]["index_patterns"]).to eq(["logstash-*"])
536
+ expect(@es.indices.get_template(name: template_name)[template_name]["settings"]['index']['lifecycle']).to be_nil
537
+ end
538
+ end
539
+
540
+ end
541
+ end
542
+ end