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.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
- data/.github/workflows/issue-auto-closer.yml +12 -0
- data/.github/workflows/linux.yml +26 -0
- data/.github/workflows/macos.yml +26 -0
- data/.github/workflows/windows.yml +26 -0
- data/.travis.yml +33 -6
- data/CONTRIBUTING.md +24 -0
- data/Gemfile +4 -1
- data/History.md +445 -1
- data/ISSUE_TEMPLATE.md +19 -0
- data/README.ElasticsearchGenID.md +116 -0
- data/README.ElasticsearchInput.md +293 -0
- data/README.Troubleshooting.md +692 -0
- data/README.md +1013 -38
- data/appveyor.yml +20 -0
- data/fluent-plugin-elasticsearch.gemspec +15 -9
- data/{Gemfile.v0.12 → gemfiles/Gemfile.elasticsearch.v6} +6 -5
- data/lib/fluent/log-ext.rb +38 -0
- data/lib/fluent/plugin/default-ilm-policy.json +14 -0
- data/lib/fluent/plugin/elasticsearch_constants.rb +13 -0
- data/lib/fluent/plugin/elasticsearch_error.rb +5 -0
- data/lib/fluent/plugin/elasticsearch_error_handler.rb +129 -0
- data/lib/fluent/plugin/elasticsearch_fallback_selector.rb +9 -0
- data/lib/fluent/plugin/elasticsearch_index_lifecycle_management.rb +67 -0
- data/lib/fluent/plugin/elasticsearch_index_template.rb +186 -12
- data/lib/fluent/plugin/elasticsearch_simple_sniffer.rb +10 -0
- data/lib/fluent/plugin/elasticsearch_tls.rb +70 -0
- data/lib/fluent/plugin/filter_elasticsearch_genid.rb +77 -0
- data/lib/fluent/plugin/in_elasticsearch.rb +325 -0
- data/lib/fluent/plugin/oj_serializer.rb +22 -0
- data/lib/fluent/plugin/out_elasticsearch.rb +1008 -267
- data/lib/fluent/plugin/out_elasticsearch_data_stream.rb +218 -0
- data/lib/fluent/plugin/out_elasticsearch_dynamic.rb +232 -214
- data/test/plugin/test_alias_template.json +9 -0
- data/test/plugin/test_elasticsearch_error_handler.rb +646 -0
- data/test/plugin/test_elasticsearch_fallback_selector.rb +74 -0
- data/test/plugin/test_elasticsearch_index_lifecycle_management.rb +66 -0
- data/test/plugin/test_elasticsearch_tls.rb +145 -0
- data/test/plugin/test_filter_elasticsearch_genid.rb +215 -0
- data/test/plugin/test_in_elasticsearch.rb +459 -0
- data/test/plugin/test_index_alias_template.json +11 -0
- data/test/plugin/test_index_template.json +25 -0
- data/test/plugin/test_oj_serializer.rb +19 -0
- data/test/plugin/test_out_elasticsearch.rb +5029 -387
- data/test/plugin/test_out_elasticsearch_data_stream.rb +337 -0
- data/test/plugin/test_out_elasticsearch_dynamic.rb +681 -208
- data/test/test_log-ext.rb +35 -0
- 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 = '
|
7
|
-
s.authors = ['diogo', 'pitr']
|
8
|
-
s.email = ['pitr.vern@gmail.com', 'me@diogoterror.com']
|
9
|
-
s.description = %q{
|
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 = '
|
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.
|
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.
|
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 '
|
28
|
-
s.add_development_dependency '
|
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
|
-
|
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,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,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,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
|
-
|
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
|
12
|
-
|
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
|
19
|
-
|
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
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
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
|
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
|