fluent-plugin-elasticsearch 1.9.4 → 5.0.3

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 (50) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  4. data/.github/workflows/issue-auto-closer.yml +12 -0
  5. data/.github/workflows/linux.yml +26 -0
  6. data/.github/workflows/macos.yml +26 -0
  7. data/.github/workflows/windows.yml +26 -0
  8. data/.travis.yml +33 -6
  9. data/CONTRIBUTING.md +24 -0
  10. data/Gemfile +4 -1
  11. data/History.md +445 -1
  12. data/ISSUE_TEMPLATE.md +19 -0
  13. data/README.ElasticsearchGenID.md +116 -0
  14. data/README.ElasticsearchInput.md +293 -0
  15. data/README.Troubleshooting.md +692 -0
  16. data/README.md +1013 -38
  17. data/appveyor.yml +20 -0
  18. data/fluent-plugin-elasticsearch.gemspec +15 -9
  19. data/{Gemfile.v0.12 → gemfiles/Gemfile.elasticsearch.v6} +6 -5
  20. data/lib/fluent/log-ext.rb +38 -0
  21. data/lib/fluent/plugin/default-ilm-policy.json +14 -0
  22. data/lib/fluent/plugin/elasticsearch_constants.rb +13 -0
  23. data/lib/fluent/plugin/elasticsearch_error.rb +5 -0
  24. data/lib/fluent/plugin/elasticsearch_error_handler.rb +129 -0
  25. data/lib/fluent/plugin/elasticsearch_fallback_selector.rb +9 -0
  26. data/lib/fluent/plugin/elasticsearch_index_lifecycle_management.rb +67 -0
  27. data/lib/fluent/plugin/elasticsearch_index_template.rb +186 -12
  28. data/lib/fluent/plugin/elasticsearch_simple_sniffer.rb +10 -0
  29. data/lib/fluent/plugin/elasticsearch_tls.rb +70 -0
  30. data/lib/fluent/plugin/filter_elasticsearch_genid.rb +77 -0
  31. data/lib/fluent/plugin/in_elasticsearch.rb +325 -0
  32. data/lib/fluent/plugin/oj_serializer.rb +22 -0
  33. data/lib/fluent/plugin/out_elasticsearch.rb +1008 -267
  34. data/lib/fluent/plugin/out_elasticsearch_data_stream.rb +218 -0
  35. data/lib/fluent/plugin/out_elasticsearch_dynamic.rb +232 -214
  36. data/test/plugin/test_alias_template.json +9 -0
  37. data/test/plugin/test_elasticsearch_error_handler.rb +646 -0
  38. data/test/plugin/test_elasticsearch_fallback_selector.rb +74 -0
  39. data/test/plugin/test_elasticsearch_index_lifecycle_management.rb +66 -0
  40. data/test/plugin/test_elasticsearch_tls.rb +145 -0
  41. data/test/plugin/test_filter_elasticsearch_genid.rb +215 -0
  42. data/test/plugin/test_in_elasticsearch.rb +459 -0
  43. data/test/plugin/test_index_alias_template.json +11 -0
  44. data/test/plugin/test_index_template.json +25 -0
  45. data/test/plugin/test_oj_serializer.rb +19 -0
  46. data/test/plugin/test_out_elasticsearch.rb +5029 -387
  47. data/test/plugin/test_out_elasticsearch_data_stream.rb +337 -0
  48. data/test/plugin/test_out_elasticsearch_dynamic.rb +681 -208
  49. data/test/test_log-ext.rb +35 -0
  50. metadata +97 -19
data/appveyor.yml ADDED
@@ -0,0 +1,20 @@
1
+ version: '{build}'
2
+ install:
3
+ - SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
4
+ - ridk.cmd enable
5
+ - ruby --version
6
+ - gem --version
7
+ - bundle install
8
+ build: off
9
+ test_script:
10
+ - bundle exec rake test
11
+
12
+ # https://www.appveyor.com/docs/installed-software/#ruby
13
+ environment:
14
+ matrix:
15
+ - ruby_version: "26-x64"
16
+ - ruby_version: "26"
17
+ - ruby_version: "25-x64"
18
+ - ruby_version: "25"
19
+ - ruby_version: "24-x64"
20
+ - ruby_version: "24"
@@ -3,28 +3,34 @@ $:.push File.expand_path('../lib', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = 'fluent-plugin-elasticsearch'
6
- s.version = '1.9.4'
7
- s.authors = ['diogo', 'pitr']
8
- s.email = ['pitr.vern@gmail.com', 'me@diogoterror.com']
9
- s.description = %q{ElasticSearch output plugin for Fluent event collector}
6
+ s.version = '5.0.3'
7
+ s.authors = ['diogo', 'pitr', 'Hiroshi Hatake']
8
+ s.email = ['pitr.vern@gmail.com', 'me@diogoterror.com', 'cosmo0920.wp@gmail.com']
9
+ s.description = %q{Elasticsearch output plugin for Fluent event collector}
10
10
  s.summary = s.description
11
11
  s.homepage = 'https://github.com/uken/fluent-plugin-elasticsearch'
12
- s.license = 'MIT'
12
+ s.license = 'Apache-2.0'
13
13
 
14
14
  s.files = `git ls-files`.split($/)
15
15
  s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
16
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
17
17
  s.require_paths = ['lib']
18
18
 
19
- s.required_ruby_version = Gem::Requirement.new(">= 2.0".freeze)
19
+ if s.respond_to?(:metadata)
20
+ s.metadata["changelog_uri"] = "https://github.com/uken/fluent-plugin-elasticsearch/blob/master/History.md"
21
+ end
20
22
 
21
- s.add_runtime_dependency 'fluentd', '>= 0.10.43'
23
+ s.required_ruby_version = Gem::Requirement.new(">= 2.3".freeze)
24
+
25
+ s.add_runtime_dependency 'fluentd', '>= 0.14.22'
22
26
  s.add_runtime_dependency 'excon', '>= 0'
23
27
  s.add_runtime_dependency 'elasticsearch'
24
28
 
25
29
 
26
30
  s.add_development_dependency 'rake', '>= 0'
27
- s.add_development_dependency 'webmock', '~> 1'
28
- s.add_development_dependency 'test-unit', '~> 3.1.0'
31
+ s.add_development_dependency 'webrick', '~> 1.7.0'
32
+ s.add_development_dependency 'webmock', '~> 3'
33
+ s.add_development_dependency 'test-unit', '~> 3.3.0'
29
34
  s.add_development_dependency 'minitest', '~> 5.8'
35
+ s.add_development_dependency 'flexmock', '~> 2.0'
30
36
  end
@@ -1,11 +1,12 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in fluent-plugin-elasticsearch.gemspec
4
- gem 'fluentd', '~> 0.12.0'
5
-
6
- gemspec
7
-
4
+ gemspec :path => "../"
8
5
 
9
6
  gem 'simplecov', require: false
10
- gem 'coveralls', require: false
7
+ gem 'coveralls', ">= 0.8.0", require: false
11
8
  gem 'strptime', require: false if RUBY_ENGINE == "ruby" && RUBY_VERSION =~ /^2/
9
+ gem "irb" if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.6"
10
+ gem "elasticsearch", "~> 6.8.1"
11
+ gem "elasticsearch-xpack"
12
+ gem "oj"
@@ -0,0 +1,38 @@
1
+ require 'fluent/log'
2
+ # For elasticsearch-ruby v7.0.0 or later
3
+ # logger for Elasticsearch::Loggable required the following methods:
4
+ #
5
+ # * debug?
6
+ # * info?
7
+ # * warn?
8
+ # * error?
9
+ # * fatal?
10
+
11
+ module Fluent
12
+ class Log
13
+ # Elasticsearch::Loggable does not request trace? method.
14
+ # def trace?
15
+ # @level <= LEVEL_TRACE
16
+ # end
17
+
18
+ def debug?
19
+ @level <= LEVEL_DEBUG
20
+ end
21
+
22
+ def info?
23
+ @level <= LEVEL_INFO
24
+ end
25
+
26
+ def warn?
27
+ @level <= LEVEL_WARN
28
+ end
29
+
30
+ def error?
31
+ @level <= LEVEL_ERROR
32
+ end
33
+
34
+ def fatal?
35
+ @level <= LEVEL_FATAL
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,14 @@
1
+ {
2
+ "policy": {
3
+ "phases": {
4
+ "hot": {
5
+ "actions": {
6
+ "rollover": {
7
+ "max_size": "50gb",
8
+ "max_age": "30d"
9
+ }
10
+ }
11
+ }
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,13 @@
1
+ module Fluent
2
+ module Plugin
3
+ module ElasticsearchConstants
4
+ BODY_DELIMITER = "\n".freeze
5
+ UPDATE_OP = "update".freeze
6
+ UPSERT_OP = "upsert".freeze
7
+ CREATE_OP = "create".freeze
8
+ INDEX_OP = "index".freeze
9
+ ID_FIELD = "_id".freeze
10
+ TIMESTAMP_FIELD = "@timestamp".freeze
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ require 'fluent/error'
2
+
3
+ class Fluent::Plugin::ElasticsearchError
4
+ class RetryableOperationExhaustedFailure < Fluent::UnrecoverableError; end
5
+ end
@@ -0,0 +1,129 @@
1
+ require 'fluent/event'
2
+ require 'fluent/error'
3
+ require_relative 'elasticsearch_constants'
4
+
5
+ class Fluent::Plugin::ElasticsearchErrorHandler
6
+ include Fluent::Plugin::ElasticsearchConstants
7
+
8
+ attr_accessor :bulk_message_count
9
+ class ElasticsearchVersionMismatch < Fluent::UnrecoverableError; end
10
+ class ElasticsearchSubmitMismatch < Fluent::UnrecoverableError; end
11
+ class ElasticsearchRequestAbortError < Fluent::UnrecoverableError; end
12
+ class ElasticsearchError < StandardError; end
13
+
14
+ def initialize(plugin)
15
+ @plugin = plugin
16
+ end
17
+
18
+ def unrecoverable_error_types
19
+ @plugin.unrecoverable_error_types
20
+ end
21
+
22
+ def unrecoverable_error?(type)
23
+ unrecoverable_error_types.include?(type)
24
+ end
25
+
26
+ def log_es_400_reason(&block)
27
+ if @plugin.log_es_400_reason
28
+ block.call
29
+ else
30
+ @plugin.log.on_debug(&block)
31
+ end
32
+ end
33
+
34
+ def handle_error(response, tag, chunk, bulk_message_count, extracted_values)
35
+ items = response['items']
36
+ if items.nil? || !items.is_a?(Array)
37
+ raise ElasticsearchVersionMismatch, "The response format was unrecognized: #{response}"
38
+ end
39
+ if bulk_message_count != items.length
40
+ raise ElasticsearchSubmitMismatch, "The number of records submitted #{bulk_message_count} do not match the number returned #{items.length}. Unable to process bulk response."
41
+ end
42
+ retry_stream = Fluent::MultiEventStream.new
43
+ stats = Hash.new(0)
44
+ meta = {}
45
+ header = {}
46
+ chunk.msgpack_each do |time, rawrecord|
47
+ bulk_message = ''
48
+ next unless rawrecord.is_a? Hash
49
+ begin
50
+ # we need a deep copy for process_message to alter
51
+ processrecord = Marshal.load(Marshal.dump(rawrecord))
52
+ meta, header, record = @plugin.process_message(tag, meta, header, time, processrecord, extracted_values)
53
+ next unless @plugin.append_record_to_messages(@plugin.write_operation, meta, header, record, bulk_message)
54
+ rescue => e
55
+ stats[:bad_chunk_record] += 1
56
+ next
57
+ end
58
+ item = items.shift
59
+ if item.is_a?(Hash) && item.has_key?(@plugin.write_operation)
60
+ write_operation = @plugin.write_operation
61
+ elsif INDEX_OP == @plugin.write_operation && item.is_a?(Hash) && item.has_key?(CREATE_OP)
62
+ write_operation = CREATE_OP
63
+ elsif UPSERT_OP == @plugin.write_operation && item.is_a?(Hash) && item.has_key?(UPDATE_OP)
64
+ write_operation = UPDATE_OP
65
+ elsif item.nil?
66
+ stats[:errors_nil_resp] += 1
67
+ next
68
+ else
69
+ # When we don't have an expected ops field, something changed in the API
70
+ # expected return values (ES 2.x)
71
+ stats[:errors_bad_resp] += 1
72
+ next
73
+ end
74
+ if item[write_operation].has_key?('status')
75
+ status = item[write_operation]['status']
76
+ else
77
+ # When we don't have a status field, something changed in the API
78
+ # expected return values (ES 2.x)
79
+ stats[:errors_bad_resp] += 1
80
+ next
81
+ end
82
+ case
83
+ when [200, 201].include?(status)
84
+ stats[:successes] += 1
85
+ when CREATE_OP == write_operation && 409 == status
86
+ stats[:duplicates] += 1
87
+ when 400 == status
88
+ stats[:bad_argument] += 1
89
+ reason = ""
90
+ log_es_400_reason do
91
+ if item[write_operation].has_key?('error') && item[write_operation]['error'].has_key?('type')
92
+ reason = " [error type]: #{item[write_operation]['error']['type']}"
93
+ end
94
+ if item[write_operation].has_key?('error') && item[write_operation]['error'].has_key?('reason')
95
+ reason += " [reason]: \'#{item[write_operation]['error']['reason']}\'"
96
+ end
97
+ end
98
+ @plugin.router.emit_error_event(tag, time, rawrecord, ElasticsearchError.new("400 - Rejected by Elasticsearch#{reason}"))
99
+ else
100
+ if item[write_operation]['error'].is_a?(String)
101
+ reason = item[write_operation]['error']
102
+ stats[:errors_block_resp] += 1
103
+ @plugin.router.emit_error_event(tag, time, rawrecord, ElasticsearchError.new("#{status} - #{reason}"))
104
+ next
105
+ elsif item[write_operation].has_key?('error') && item[write_operation]['error'].has_key?('type')
106
+ type = item[write_operation]['error']['type']
107
+ stats[type] += 1
108
+ retry_stream.add(time, rawrecord)
109
+ if unrecoverable_error?(type)
110
+ raise ElasticsearchRequestAbortError, "Rejected Elasticsearch due to #{type}"
111
+ end
112
+ else
113
+ # When we don't have a type field, something changed in the API
114
+ # expected return values (ES 2.x)
115
+ stats[:errors_bad_resp] += 1
116
+ @plugin.router.emit_error_event(tag, time, rawrecord, ElasticsearchError.new("#{status} - No error type provided in the response"))
117
+ next
118
+ end
119
+ stats[type] += 1
120
+ end
121
+ end
122
+ @plugin.log.on_debug do
123
+ msg = ["Indexed (op = #{@plugin.write_operation})"]
124
+ stats.each_pair { |key, value| msg << "#{value} #{key}" }
125
+ @plugin.log.debug msg.join(', ')
126
+ end
127
+ raise Fluent::Plugin::ElasticsearchOutput::RetryStreamError.new(retry_stream) unless retry_stream.empty?
128
+ end
129
+ end
@@ -0,0 +1,9 @@
1
+ require 'elasticsearch/transport/transport/connections/selector'
2
+
3
+ class Fluent::Plugin::ElasticseatchFallbackSelector
4
+ include Elasticsearch::Transport::Transport::Connections::Selector::Base
5
+
6
+ def select(options={})
7
+ connections.first
8
+ end
9
+ end
@@ -0,0 +1,67 @@
1
+ module Fluent::Plugin::ElasticsearchIndexLifecycleManagement
2
+ ILM_DEFAULT_POLICY_PATH = "default-ilm-policy.json"
3
+
4
+ def setup_ilm(enable_ilm, policy_id, ilm_policy = default_policy_payload, overwrite = false)
5
+ return unless enable_ilm
6
+
7
+ create_ilm_policy(policy_id, ilm_policy, overwrite)
8
+ end
9
+
10
+ def verify_ilm_working
11
+ # Check the Elasticsearch instance for ILM readiness - this means that the version has to be a non-OSS release, with ILM feature
12
+ # available and enabled.
13
+ begin
14
+ xpack = xpack_info
15
+ if xpack.nil?
16
+ raise Fluent::ConfigError, "xpack endpoint does not work"
17
+ end
18
+ features = xpack["features"]
19
+ ilm = features.nil? ? nil : features["ilm"]
20
+ raise Fluent::ConfigError, "Index Lifecycle management is enabled in Fluentd, but not installed on your Elasticsearch" if features.nil? || ilm.nil?
21
+ raise Fluent::ConfigError, "Index Lifecycle management is enabled in Fluentd, but not available in your Elasticsearch" unless ilm['available']
22
+ raise Fluent::ConfigError, "Index Lifecycle management is enabled in Fluentd, but not enabled in your Elasticsearch" unless ilm['enabled']
23
+
24
+ rescue Elasticsearch::Transport::Transport::Error => e
25
+ raise Fluent::ConfigError, "Index Lifecycle management is enabled in Fluentd, but not installed on your Elasticsearch", error: e
26
+ end
27
+ end
28
+
29
+ def create_ilm_policy(policy_id, ilm_policy = default_policy_payload, overwrite = false)
30
+ if overwrite || !ilm_policy_exists?(policy_id)
31
+ ilm_policy_put(policy_id, ilm_policy)
32
+ end
33
+ end
34
+
35
+ def xpack_info
36
+ begin
37
+ client.xpack.info
38
+ rescue NoMethodError
39
+ raise RuntimeError, "elasticsearch-xpack gem is not installed."
40
+ rescue
41
+ nil
42
+ end
43
+ end
44
+
45
+ def get_ilm_policy
46
+ client.ilm.get_policy
47
+ end
48
+
49
+ def ilm_policy_exists?(policy_id)
50
+ begin
51
+ client.ilm.get_policy(policy_id: policy_id)
52
+ true
53
+ rescue
54
+ false
55
+ end
56
+ end
57
+
58
+ def ilm_policy_put(policy_id, policy)
59
+ log.info("Installing ILM policy: #{policy}")
60
+ client.ilm.put_policy(policy_id: policy_id, body: policy)
61
+ end
62
+
63
+ def default_policy_payload
64
+ default_policy_path = File.join(__dir__, ILM_DEFAULT_POLICY_PATH)
65
+ Yajl.load(::IO.read(default_policy_path))
66
+ end
67
+ end
@@ -1,5 +1,7 @@
1
- module Fluent::ElasticsearchIndexTemplate
1
+ require 'fluent/error'
2
+ require_relative './elasticsearch_error'
2
3
 
4
+ module Fluent::ElasticsearchIndexTemplate
3
5
  def get_template(template_file)
4
6
  if !File.exists?(template_file)
5
7
  raise "If you specify a template_name you must specify a valid template file (checked '#{template_file}')!"
@@ -8,30 +10,202 @@ module Fluent::ElasticsearchIndexTemplate
8
10
  JSON.parse(file_contents)
9
11
  end
10
12
 
11
- def template_exists?(name)
12
- client.indices.get_template(:name => name)
13
+ def get_custom_template(template_file, customize_template)
14
+ if !File.exists?(template_file)
15
+ raise "If you specify a template_name you must specify a valid template file (checked '#{template_file}')!"
16
+ end
17
+ file_contents = IO.read(template_file).gsub(/\n/,'')
18
+ customize_template.each do |key, value|
19
+ file_contents = file_contents.gsub(key,value.downcase)
20
+ end
21
+ JSON.parse(file_contents)
22
+ end
23
+
24
+ def template_exists?(name, host = nil)
25
+ if @use_legacy_template
26
+ client(host).indices.get_template(:name => name)
27
+ else
28
+ client(host).indices.get_index_template(:name => name)
29
+ end
13
30
  return true
14
31
  rescue Elasticsearch::Transport::Transport::Errors::NotFound
15
32
  return false
16
33
  end
17
34
 
18
- def template_put(name, template)
19
- client.indices.put_template(:name => name, :body => template)
35
+ def retry_operate(max_retries, fail_on_retry_exceed = true, catch_trasport_exceptions = true)
36
+ return unless block_given?
37
+ retries = 0
38
+ transport_errors = Elasticsearch::Transport::Transport::Errors.constants.map{ |c| Elasticsearch::Transport::Transport::Errors.const_get c } if catch_trasport_exceptions
39
+ begin
40
+ yield
41
+ rescue *client.transport.host_unreachable_exceptions, *transport_errors, Timeout::Error => e
42
+ @_es = nil
43
+ @_es_info = nil
44
+ if retries < max_retries
45
+ retries += 1
46
+ wait_seconds = 2**retries
47
+ sleep wait_seconds
48
+ log.warn "Could not communicate to Elasticsearch, resetting connection and trying again. #{e.message}"
49
+ log.warn "Remaining retry: #{max_retries - retries}. Retry to communicate after #{wait_seconds} second(s)."
50
+ retry
51
+ end
52
+ message = "Could not communicate to Elasticsearch after #{retries} retries. #{e.message}"
53
+ log.warn message
54
+ raise Fluent::Plugin::ElasticsearchError::RetryableOperationExhaustedFailure,
55
+ message if fail_on_retry_exceed
56
+ end
57
+ end
58
+
59
+ def template_put(name, template, host = nil)
60
+ if @use_legacy_template
61
+ client(host).indices.put_template(:name => name, :body => template)
62
+ else
63
+ client(host).indices.put_index_template(:name => name, :body => template)
64
+ end
65
+ end
66
+
67
+ def indexcreation(index_name, host = nil)
68
+ client(host).indices.create(:index => index_name)
69
+ rescue Elasticsearch::Transport::Transport::Error => e
70
+ if e.message =~ /"already exists"/ || e.message =~ /resource_already_exists_exception/
71
+ log.debug("Index #{index_name} already exists")
72
+ else
73
+ log.error("Error while index creation - #{index_name}", error: e)
74
+ end
75
+ end
76
+
77
+ def template_install(name, template_file, overwrite, enable_ilm = false, deflector_alias_name = nil, ilm_policy_id = nil, host = nil, target_index = nil, index_separator = '-')
78
+ inject_template_name = get_template_name(enable_ilm, name, deflector_alias_name)
79
+ if overwrite
80
+ template_put(inject_template_name,
81
+ enable_ilm ? inject_ilm_settings_to_template(deflector_alias_name,
82
+ target_index,
83
+ ilm_policy_id,
84
+ get_template(template_file),
85
+ index_separator) :
86
+ get_template(template_file), host)
87
+
88
+ log.debug("Template '#{inject_template_name}' overwritten with #{template_file}.")
89
+ return
90
+ end
91
+ if !template_exists?(inject_template_name, host)
92
+ template_put(inject_template_name,
93
+ enable_ilm ? inject_ilm_settings_to_template(deflector_alias_name,
94
+ target_index,
95
+ ilm_policy_id,
96
+ get_template(template_file),
97
+ index_separator) :
98
+ get_template(template_file), host)
99
+ log.info("Template configured, but no template installed. Installed '#{inject_template_name}' from #{template_file}.")
100
+ else
101
+ log.debug("Template '#{inject_template_name}' configured and already installed.")
102
+ end
20
103
  end
21
104
 
22
- def template_install(name, template_file)
23
- if !template_exists?(name)
24
- template_put(name, get_template(template_file))
25
- log.info("Template configured, but no template installed. Installed '#{name}' from #{template_file}.")
105
+ def template_custom_install(template_name, template_file, overwrite, customize_template, enable_ilm, deflector_alias_name, ilm_policy_id, host, target_index, index_separator)
106
+ template_custom_name = get_template_name(enable_ilm, template_name, deflector_alias_name)
107
+ custom_template = if enable_ilm
108
+ inject_ilm_settings_to_template(deflector_alias_name,
109
+ target_index,
110
+ ilm_policy_id,
111
+ get_custom_template(template_file,
112
+ customize_template),
113
+ index_separator)
114
+ else
115
+ get_custom_template(template_file, customize_template)
116
+ end
117
+ if overwrite
118
+ template_put(template_custom_name, custom_template, host)
119
+ log.info("Template '#{template_custom_name}' overwritten with #{template_file}.")
26
120
  else
27
- log.info("Template configured and already installed.")
121
+ if !template_exists?(template_custom_name, host)
122
+ template_put(template_custom_name, custom_template, host)
123
+ log.info("Template configured, but no template installed. Installed '#{template_custom_name}' from #{template_file}.")
124
+ else
125
+ log.debug("Template '#{template_custom_name}' configured and already installed.")
126
+ end
28
127
  end
29
128
  end
30
129
 
31
- def templates_hash_install (templates)
130
+ def get_template_name(enable_ilm, template_name, deflector_alias_name)
131
+ enable_ilm ? deflector_alias_name : template_name
132
+ end
133
+
134
+ def inject_ilm_settings_to_template(deflector_alias, target_index, ilm_policy_id, template, index_separator)
135
+ log.debug("Overwriting index patterns when Index Lifecycle Management is enabled.")
136
+ template['index_patterns'] = "#{target_index}#{index_separator}*"
137
+ if @use_legacy_template
138
+ template.delete('template') if template.include?('template')
139
+ # Prepare settings Hash
140
+ if !template.key?('settings')
141
+ template['settings'] = {}
142
+ end
143
+ if template['settings'] && (template['settings']['index.lifecycle.name'] || template['settings']['index.lifecycle.rollover_alias'])
144
+ log.debug("Overwriting index lifecycle name and rollover alias when Index Lifecycle Management is enabled.")
145
+ end
146
+ template['settings'].update({ 'index.lifecycle.name' => ilm_policy_id, 'index.lifecycle.rollover_alias' => deflector_alias})
147
+ template['order'] = template['order'] ? template['order'] + target_index.count(index_separator) + 1 : 51 + target_index.count(index_separator)
148
+ else
149
+ # Prepare template.settings Hash
150
+ if !template['template'].key?('settings')
151
+ template['template']['settings'] = {}
152
+ end
153
+ if template['template']['settings'] && (template['template']['settings']['index.lifecycle.name'] || template['template']['settings']['index.lifecycle.rollover_alias'])
154
+ log.debug("Overwriting index lifecycle name and rollover alias when Index Lifecycle Management is enabled.")
155
+ end
156
+ template['template']['settings'].update({ 'index.lifecycle.name' => ilm_policy_id, 'index.lifecycle.rollover_alias' => deflector_alias})
157
+ template['priority'] = template['priority'] ? template['priority'] + target_index.count(index_separator) + 1 : 101 + target_index.count(index_separator)
158
+ end
159
+ template
160
+ end
161
+
162
+ def create_rollover_alias(target_index, rollover_index, deflector_alias_name, app_name, index_date_pattern, index_separator, enable_ilm, ilm_policy_id, ilm_policy, ilm_policy_overwrite, host)
163
+ # ILM request to create alias.
164
+ if rollover_index || enable_ilm
165
+ if !client.indices.exists_alias(:name => deflector_alias_name)
166
+ if @logstash_format
167
+ index_name_temp = '<'+target_index+'-000001>'
168
+ else
169
+ if index_date_pattern.empty?
170
+ index_name_temp = '<'+target_index.downcase+index_separator+app_name.downcase+'-000001>'
171
+ else
172
+ index_name_temp = '<'+target_index.downcase+index_separator+app_name.downcase+'-{'+index_date_pattern+'}-000001>'
173
+ end
174
+ end
175
+ indexcreation(index_name_temp, host)
176
+ body = rollover_alias_payload(deflector_alias_name)
177
+ client.indices.put_alias(:index => index_name_temp, :name => deflector_alias_name,
178
+ :body => body)
179
+ log.info("The alias '#{deflector_alias_name}' is created for the index '#{index_name_temp}'")
180
+ else
181
+ log.debug("The alias '#{deflector_alias_name}' is already present")
182
+ end
183
+ # Create ILM policy if rollover indices exist.
184
+ if enable_ilm
185
+ if ilm_policy.empty?
186
+ setup_ilm(enable_ilm, ilm_policy_id)
187
+ else
188
+ setup_ilm(enable_ilm, ilm_policy_id, ilm_policy, ilm_policy_overwrite)
189
+ end
190
+ end
191
+ else
192
+ log.debug("No index and alias creation action performed because rollover_index or enable_ilm is set to: '#{rollover_index}', '#{enable_ilm}'")
193
+ end
194
+ end
195
+
196
+ def templates_hash_install(templates, overwrite)
32
197
  templates.each do |key, value|
33
- template_install(key, value)
198
+ template_install(key, value, overwrite)
34
199
  end
35
200
  end
36
201
 
202
+ def rollover_alias_payload(rollover_alias)
203
+ {
204
+ 'aliases' => {
205
+ rollover_alias => {
206
+ 'is_write_index' => true
207
+ }
208
+ }
209
+ }
210
+ end
37
211
  end
@@ -0,0 +1,10 @@
1
+ require 'elasticsearch'
2
+
3
+ class Fluent::Plugin::ElasticsearchSimpleSniffer < Elasticsearch::Transport::Transport::Sniffer
4
+
5
+ def hosts
6
+ @transport.logger.debug "In Fluent::Plugin::ElasticsearchSimpleSniffer hosts #{@transport.hosts}" if @transport.logger
7
+ @transport.hosts
8
+ end
9
+
10
+ end
@@ -0,0 +1,70 @@
1
+ require 'openssl'
2
+ require 'fluent/configurable'
3
+ require 'fluent/config/error'
4
+
5
+ module Fluent::Plugin
6
+ module ElasticsearchTLS
7
+ SUPPORTED_TLS_VERSIONS = if defined?(OpenSSL::SSL::TLS1_3_VERSION)
8
+ [:TLSv1, :TLSv1_1, :TLSv1_2, :TLSv1_3].freeze
9
+ else
10
+ [:SSLv23, :TLSv1, :TLSv1_1, :TLSv1_2].freeze
11
+ end
12
+
13
+ DEFAULT_VERSION = :TLSv1_2
14
+ METHODS_MAP = begin
15
+ # When openssl supports OpenSSL::SSL::TLSXXX constants representations, we use them.
16
+ map = {
17
+ TLSv1: OpenSSL::SSL::TLS1_VERSION,
18
+ TLSv1_1: OpenSSL::SSL::TLS1_1_VERSION,
19
+ TLSv1_2: OpenSSL::SSL::TLS1_2_VERSION
20
+ }
21
+ map[:TLSv1_3] = OpenSSL::SSL::TLS1_3_VERSION if defined?(OpenSSL::SSL::TLS1_3_VERSION)
22
+ USE_TLS_MINMAX_VERSION = true
23
+ map.freeze
24
+ rescue NameError
25
+ map = {
26
+ SSLv23: :SSLv23,
27
+ TLSv1: :TLSv1,
28
+ TLSv1_1: :TLSv1_1,
29
+ TLSv1_2: :TLSv1_2,
30
+ }
31
+ USE_TLS_MINMAX_VERSION = false
32
+ end
33
+ private_constant :METHODS_MAP
34
+
35
+ module ElasticsearchTLSParams
36
+ include Fluent::Configurable
37
+
38
+ config_param :ssl_version, :enum, list: Fluent::Plugin::ElasticsearchTLS::SUPPORTED_TLS_VERSIONS, default: Fluent::Plugin::ElasticsearchTLS::DEFAULT_VERSION
39
+ config_param :ssl_min_version, :enum, list: Fluent::Plugin::ElasticsearchTLS::SUPPORTED_TLS_VERSIONS, default: nil
40
+ config_param :ssl_max_version, :enum, list: Fluent::Plugin::ElasticsearchTLS::SUPPORTED_TLS_VERSIONS, default: nil
41
+ end
42
+
43
+ def self.included(mod)
44
+ mod.include ElasticsearchTLSParams
45
+ end
46
+
47
+ def set_tls_minmax_version_config(ssl_version, ssl_max_version, ssl_min_version)
48
+ if USE_TLS_MINMAX_VERSION
49
+ case
50
+ when ssl_min_version.nil? && ssl_max_version.nil?
51
+ ssl_min_version = METHODS_MAP[:TLSv1_2]
52
+ ssl_max_version = METHODS_MAP[:TLSv1_3]
53
+ when ssl_min_version && ssl_max_version.nil?
54
+ raise Fluent::ConfigError, "When you set 'ssl_min_version', must set 'ssl_max_version' together."
55
+ when ssl_min_version.nil? && ssl_max_version
56
+ raise Fluent::ConfigError, "When you set 'ssl_max_version', must set 'ssl_min_version' together."
57
+ else
58
+ ssl_min_version = METHODS_MAP[ssl_min_version]
59
+ ssl_max_version = METHODS_MAP[ssl_max_version]
60
+ end
61
+
62
+ {max_version: ssl_max_version, min_version: ssl_min_version}
63
+ else
64
+ log.warn "'ssl_min_version' does not have any effect in this environment. Use 'ssl_version' instead." unless ssl_min_version.nil?
65
+ log.warn "'ssl_max_version' does not have any effect in this environment. Use 'ssl_version' instead." unless ssl_max_version.nil?
66
+ {version: ssl_version}
67
+ end
68
+ end
69
+ end
70
+ end