test-opensearch 0.0.0.1

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 (52) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.editorconfig +9 -0
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  6. data/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +9 -0
  7. data/.github/workflows/coverage.yaml +22 -0
  8. data/.github/workflows/issue-auto-closer.yml +12 -0
  9. data/.github/workflows/linux.yml +26 -0
  10. data/.github/workflows/macos.yml +26 -0
  11. data/.github/workflows/windows.yml +26 -0
  12. data/.gitignore +18 -0
  13. data/CONTRIBUTING.md +24 -0
  14. data/Gemfile +10 -0
  15. data/History.md +67 -0
  16. data/LICENSE.txt +201 -0
  17. data/README.OpenSearchGenID.md +116 -0
  18. data/README.OpenSearchInput.md +314 -0
  19. data/README.Troubleshooting.md +482 -0
  20. data/README.md +1622 -0
  21. data/Rakefile +37 -0
  22. data/fluent-plugin-opensearch.gemspec +39 -0
  23. data/gemfiles/Gemfile.elasticsearch.v6 +12 -0
  24. data/lib/fluent/log-ext.rb +64 -0
  25. data/lib/fluent/plugin/filter_opensearch_genid.rb +103 -0
  26. data/lib/fluent/plugin/in_opensearch.rb +374 -0
  27. data/lib/fluent/plugin/oj_serializer.rb +48 -0
  28. data/lib/fluent/plugin/opensearch_constants.rb +39 -0
  29. data/lib/fluent/plugin/opensearch_error.rb +31 -0
  30. data/lib/fluent/plugin/opensearch_error_handler.rb +182 -0
  31. data/lib/fluent/plugin/opensearch_fallback_selector.rb +36 -0
  32. data/lib/fluent/plugin/opensearch_index_template.rb +155 -0
  33. data/lib/fluent/plugin/opensearch_simple_sniffer.rb +36 -0
  34. data/lib/fluent/plugin/opensearch_tls.rb +96 -0
  35. data/lib/fluent/plugin/out_opensearch.rb +1158 -0
  36. data/lib/fluent/plugin/out_opensearch_data_stream.rb +229 -0
  37. data/test/helper.rb +60 -0
  38. data/test/plugin/datastream_template.json +4 -0
  39. data/test/plugin/test_alias_template.json +9 -0
  40. data/test/plugin/test_filter_opensearch_genid.rb +241 -0
  41. data/test/plugin/test_in_opensearch.rb +500 -0
  42. data/test/plugin/test_index_alias_template.json +11 -0
  43. data/test/plugin/test_index_template.json +25 -0
  44. data/test/plugin/test_oj_serializer.rb +45 -0
  45. data/test/plugin/test_opensearch_error_handler.rb +770 -0
  46. data/test/plugin/test_opensearch_fallback_selector.rb +100 -0
  47. data/test/plugin/test_opensearch_tls.rb +171 -0
  48. data/test/plugin/test_out_opensearch.rb +3980 -0
  49. data/test/plugin/test_out_opensearch_data_stream.rb +746 -0
  50. data/test/plugin/test_template.json +23 -0
  51. data/test/test_log-ext.rb +61 -0
  52. metadata +291 -0
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # The fluent-plugin-opensearch Contributors require contributions made to
4
+ # this file be licensed under the Apache-2.0 license or a
5
+ # compatible open source license.
6
+ #
7
+ # Modifications Copyright fluent-plugin-opensearch Contributors. See
8
+ # GitHub history for details.
9
+ #
10
+ # Licensed to Uken Inc. under one or more contributor
11
+ # license agreements. See the NOTICE file distributed with
12
+ # this work for additional information regarding copyright
13
+ # ownership. Uken Inc. licenses this file to you under
14
+ # the Apache License, Version 2.0 (the "License"); you may
15
+ # not use this file except in compliance with the License.
16
+ # You may obtain a copy of the License at
17
+ #
18
+ # http://www.apache.org/licenses/LICENSE-2.0
19
+ #
20
+ # Unless required by applicable law or agreed to in writing,
21
+ # software distributed under the License is distributed on an
22
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23
+ # KIND, either express or implied. See the License for the
24
+ # specific language governing permissions and limitations
25
+ # under the License.
26
+
27
+ require 'bundler/gem_tasks'
28
+ require 'rake/testtask'
29
+
30
+ Rake::TestTask.new(:test) do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ test.warning = false
35
+ end
36
+
37
+ task :default => :test
@@ -0,0 +1,39 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'test-opensearch'
6
+ s.version = '0.0.0.1'
7
+ s.authors = ['Oleh']
8
+ s.email = ['imcotop@icloud.com']
9
+ s.description = %q{test}
10
+ s.summary = s.description
11
+ s.homepage = 'https://github.com/fluent/fluent-plugin-opensearch'
12
+ s.license = 'Apache-2.0'
13
+
14
+ s.files = `git ls-files`.split($/)
15
+ s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
17
+ s.require_paths = ['lib']
18
+
19
+ if s.respond_to?(:metadata)
20
+ s.metadata["changelog_uri"] = "https://github.com/fluent/fluent-plugin-opensearch/blob/master/History.md"
21
+ end
22
+
23
+ s.required_ruby_version = Gem::Requirement.new(">= 2.3".freeze)
24
+
25
+ s.add_runtime_dependency 'fluentd', '>= 0.14.22'
26
+ s.add_runtime_dependency 'opensearch-ruby', '>= 3.0.1'
27
+ s.add_runtime_dependency "aws-sdk-core", "~> 3"
28
+ s.add_runtime_dependency 'excon', '>= 0'
29
+ s.add_runtime_dependency 'faraday', '>= 2.0.0'
30
+ s.add_runtime_dependency 'faraday-excon', '>= 2.0.0'
31
+ s.add_runtime_dependency "faraday_middleware-aws-sigv4", "~> 1.0.1"
32
+
33
+ s.add_development_dependency 'rake', '>= 0'
34
+ s.add_development_dependency 'webrick', '~> 1.7.0'
35
+ s.add_development_dependency 'webmock', '~> 3.18.1'
36
+ s.add_development_dependency 'test-unit', '~> 3.3.0'
37
+ s.add_development_dependency 'minitest', '~> 5.8'
38
+ s.add_development_dependency 'flexmock', '~> 2.0'
39
+ end
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fluent-plugin-elasticsearch.gemspec
4
+ gemspec :path => "../"
5
+
6
+ gem 'simplecov', require: false
7
+ gem 'coveralls', ">= 0.8.0", require: false
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,64 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # The fluent-plugin-opensearch Contributors require contributions made to
4
+ # this file be licensed under the Apache-2.0 license or a
5
+ # compatible open source license.
6
+ #
7
+ # Modifications Copyright OpenSearch Contributors. See
8
+ # GitHub history for details.
9
+ #
10
+ # Licensed toUken Inc. under one or more contributor
11
+ # license agreements. See the NOTICE file distributed with
12
+ # this work for additional information regarding copyright
13
+ # ownership. Elasticsearch B.V. licenses this file to you under
14
+ # the Apache License, Version 2.0 (the "License"); you may
15
+ # not use this file except in compliance with the License.
16
+ # You may obtain a copy of the License at
17
+ #
18
+ # http://www.apache.org/licenses/LICENSE-2.0
19
+ #
20
+ # Unless required by applicable law or agreed to in writing,
21
+ # software distributed under the License is distributed on an
22
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23
+ # KIND, either express or implied. See the License for the
24
+ # specific language governing permissions and limitations
25
+ # under the License.
26
+
27
+ require 'fluent/log'
28
+ # For opensearch-ruby v1.0.0 or later which is based on elasticsearch-ruby v7.14 master tree
29
+ # logger for Elasticsearch::Loggable required the following methods:
30
+ #
31
+ # * debug?
32
+ # * info?
33
+ # * warn?
34
+ # * error?
35
+ # * fatal?
36
+
37
+ module Fluent
38
+ class Log
39
+ # OpenSearch::Loggable does not request trace? method.
40
+ # def trace?
41
+ # @level <= LEVEL_TRACE
42
+ # end
43
+
44
+ def debug?
45
+ @level <= LEVEL_DEBUG
46
+ end
47
+
48
+ def info?
49
+ @level <= LEVEL_INFO
50
+ end
51
+
52
+ def warn?
53
+ @level <= LEVEL_WARN
54
+ end
55
+
56
+ def error?
57
+ @level <= LEVEL_ERROR
58
+ end
59
+
60
+ def fatal?
61
+ @level <= LEVEL_FATAL
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,103 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # The fluent-plugin-opensearch Contributors require contributions made to
4
+ # this file be licensed under the Apache-2.0 license or a
5
+ # compatible open source license.
6
+ #
7
+ # Modifications Copyright fluent-plugin-opensearch Contributors. See
8
+ # GitHub history for details.
9
+ #
10
+ # Licensed to Uken Inc. under one or more contributor
11
+ # license agreements. See the NOTICE file distributed with
12
+ # this work for additional information regarding copyright
13
+ # ownership. Uken Inc. licenses this file to you under
14
+ # the Apache License, Version 2.0 (the "License"); you may
15
+ # not use this file except in compliance with the License.
16
+ # You may obtain a copy of the License at
17
+ #
18
+ # http://www.apache.org/licenses/LICENSE-2.0
19
+ #
20
+ # Unless required by applicable law or agreed to in writing,
21
+ # software distributed under the License is distributed on an
22
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23
+ # KIND, either express or implied. See the License for the
24
+ # specific language governing permissions and limitations
25
+ # under the License.
26
+
27
+ require 'securerandom'
28
+ require 'base64'
29
+ require 'fluent/plugin/filter'
30
+
31
+ module Fluent::Plugin
32
+ class OpenSearchGenidFilter < Filter
33
+ Fluent::Plugin.register_filter('opensearch_genid', self)
34
+
35
+ config_param :hash_id_key, :string, :default => '_hash'
36
+ config_param :include_tag_in_seed, :bool, :default => false
37
+ config_param :include_time_in_seed, :bool, :default => false
38
+ config_param :use_record_as_seed, :bool, :default => false
39
+ config_param :use_entire_record, :bool, :default => false
40
+ config_param :record_keys, :array, :default => []
41
+ config_param :separator, :string, :default => '_'
42
+ config_param :hash_type, :enum, list: [:md5, :sha1, :sha256, :sha512], :default => :sha1
43
+
44
+ def initialize
45
+ super
46
+ end
47
+
48
+ def configure(conf)
49
+ super
50
+
51
+ if !@use_entire_record
52
+ if @record_keys.empty? && @use_record_as_seed
53
+ raise Fluent::ConfigError, "When using record as hash seed, users must specify `record_keys`."
54
+ end
55
+ end
56
+
57
+ if @use_record_as_seed
58
+ class << self
59
+ alias_method :filter, :filter_seed_as_record
60
+ end
61
+ else
62
+ class << self
63
+ alias_method :filter, :filter_simple
64
+ end
65
+ end
66
+ end
67
+
68
+ def filter(tag, time, record)
69
+ # for safety.
70
+ end
71
+
72
+ def filter_simple(tag, time, record)
73
+ record[@hash_id_key] = Base64.strict_encode64(SecureRandom.uuid)
74
+ record
75
+ end
76
+
77
+ def filter_seed_as_record(tag, time, record)
78
+ seed = ""
79
+ seed += tag + separator if @include_tag_in_seed
80
+ seed += time.to_s + separator if @include_time_in_seed
81
+ if @use_entire_record
82
+ record.each {|k,v| seed += "|#{k}|#{v}"}
83
+ else
84
+ seed += record_keys.map {|k| record[k]}.join(separator)
85
+ end
86
+ record[@hash_id_key] = Base64.strict_encode64(encode_hash(@hash_type, seed))
87
+ record
88
+ end
89
+
90
+ def encode_hash(type, seed)
91
+ case type
92
+ when :md5
93
+ Digest::MD5.digest(seed)
94
+ when :sha1
95
+ Digest::SHA1.digest(seed)
96
+ when :sha256
97
+ Digest::SHA256.digest(seed)
98
+ when :sha512
99
+ Digest::SHA512.digest(seed)
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,374 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # The fluent-plugin-opensearch Contributors require contributions made to
4
+ # this file be licensed under the Apache-2.0 license or a
5
+ # compatible open source license.
6
+ #
7
+ # Modifications Copyright fluent-plugin-opensearch Contributors. See
8
+ # GitHub history for details.
9
+ #
10
+ # Licensed to Uken Inc. under one or more contributor
11
+ # license agreements. See the NOTICE file distributed with
12
+ # this work for additional information regarding copyright
13
+ # ownership. Uken Inc. licenses this file to you under
14
+ # the Apache License, Version 2.0 (the "License"); you may
15
+ # not use this file except in compliance with the License.
16
+ # You may obtain a copy of the License at
17
+ #
18
+ # http://www.apache.org/licenses/LICENSE-2.0
19
+ #
20
+ # Unless required by applicable law or agreed to in writing,
21
+ # software distributed under the License is distributed on an
22
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23
+ # KIND, either express or implied. See the License for the
24
+ # specific language governing permissions and limitations
25
+ # under the License.
26
+
27
+ require 'opensearch'
28
+
29
+ require 'faraday/excon'
30
+ require 'fluent/log-ext'
31
+ require 'fluent/plugin/input'
32
+ require_relative 'opensearch_constants'
33
+
34
+ module Fluent::Plugin
35
+ class OpenSearchInput < Input
36
+ class UnrecoverableRequestFailure < Fluent::UnrecoverableError; end
37
+
38
+ DEFAULT_RELOAD_AFTER = -1
39
+ DEFAULT_STORAGE_TYPE = 'local'
40
+ METADATA = "@metadata".freeze
41
+
42
+ helpers :timer, :thread
43
+
44
+ Fluent::Plugin.register_input('opensearch', self)
45
+
46
+ config_param :tag, :string
47
+ config_param :host, :string, :default => 'localhost'
48
+ config_param :port, :integer, :default => 9200
49
+ config_param :user, :string, :default => nil
50
+ config_param :password, :string, :default => nil, :secret => true
51
+ config_param :path, :string, :default => nil
52
+ config_param :scheme, :enum, :list => [:https, :http], :default => :http
53
+ config_param :hosts, :string, :default => nil
54
+ config_param :index_name, :string, :default => "fluentd"
55
+ config_param :parse_timestamp, :bool, :default => false
56
+ config_param :timestamp_key_format, :string, :default => nil
57
+ config_param :timestamp_parse_error_tag, :string, :default => 'opensearch_plugin.input.time.error'
58
+ config_param :query, :hash, :default => {"sort" => [ "_doc" ]}
59
+ config_param :scroll, :string, :default => "1m"
60
+ config_param :size, :integer, :default => 1000
61
+ config_param :num_slices, :integer, :default => 1
62
+ config_param :interval, :size, :default => 5
63
+ config_param :repeat, :bool, :default => true
64
+ config_param :http_backend, :enum, list: [:excon, :typhoeus], :default => :excon
65
+ config_param :request_timeout, :time, :default => 5
66
+ config_param :reload_connections, :bool, :default => true
67
+ config_param :reload_on_failure, :bool, :default => false
68
+ config_param :resurrect_after, :time, :default => 60
69
+ config_param :reload_after, :integer, :default => DEFAULT_RELOAD_AFTER
70
+ config_param :ssl_verify , :bool, :default => true
71
+ config_param :client_key, :string, :default => nil
72
+ config_param :client_cert, :string, :default => nil
73
+ config_param :client_key_pass, :string, :default => nil, :secret => true
74
+ config_param :ca_file, :string, :default => nil
75
+ config_param :ssl_version, :enum, list: [:SSLv23, :TLSv1, :TLSv1_1, :TLSv1_2], :default => :TLSv1_2
76
+ config_param :with_transporter_log, :bool, :default => false
77
+ config_param :emit_error_label_event, :bool, :default => true
78
+ config_param :sniffer_class_name, :string, :default => nil
79
+ config_param :custom_headers, :hash, :default => {}
80
+ config_param :docinfo_fields, :array, :default => ['_index', '_type', '_id']
81
+ config_param :docinfo_target, :string, :default => METADATA
82
+ config_param :docinfo, :bool, :default => false
83
+ config_param :infinite_check_connection, :bool, :default => true
84
+
85
+ include Fluent::Plugin::OpenSearchConstants
86
+
87
+ def initialize
88
+ super
89
+ end
90
+
91
+ def configure(conf)
92
+ super
93
+
94
+ @timestamp_parser = create_time_parser
95
+ @backend_options = backend_options
96
+
97
+ raise Fluent::ConfigError, "`password` must be present if `user` is present" if @user && @password.nil?
98
+
99
+ if @user && m = @user.match(/%{(?<user>.*)}/)
100
+ @user = URI.encode_www_form_component(m["user"])
101
+ end
102
+ if @password && m = @password.match(/%{(?<password>.*)}/)
103
+ @password = URI.encode_www_form_component(m["password"])
104
+ end
105
+
106
+ @transport_logger = nil
107
+ if @with_transporter_log
108
+ @transport_logger = log
109
+ log_level = conf['@log_level'] || conf['log_level']
110
+ log.warn "Consider to specify log_level with @log_level." unless log_level
111
+ end
112
+ @current_config = nil
113
+ # Specify @sniffer_class before calling #client.
114
+ @sniffer_class = nil
115
+ begin
116
+ @sniffer_class = Object.const_get(@sniffer_class_name) if @sniffer_class_name
117
+ rescue Exception => ex
118
+ raise Fluent::ConfigError, "Could not load sniffer class #{@sniffer_class_name}: #{ex}"
119
+ end
120
+
121
+ @options = {
122
+ :index => @index_name,
123
+ :scroll => @scroll,
124
+ :size => @size
125
+ }
126
+ @base_query = @query
127
+ end
128
+
129
+ def backend_options
130
+ case @http_backend
131
+ when :excon
132
+ { client_key: @client_key, client_cert: @client_cert, client_key_pass: @client_key_pass }
133
+ when :typhoeus
134
+ require 'typhoeus'
135
+ { sslkey: @client_key, sslcert: @client_cert, keypasswd: @client_key_pass }
136
+ end
137
+ rescue LoadError => ex
138
+ log.error_backtrace(ex.backtrace)
139
+ raise Fluent::ConfigError, "You must install #{@http_backend} gem. Exception: #{ex}"
140
+ end
141
+
142
+ def get_escaped_userinfo(host_str)
143
+ if m = host_str.match(/(?<scheme>.*)%{(?<user>.*)}:%{(?<password>.*)}(?<path>@.*)/)
144
+ m["scheme"] +
145
+ URI.encode_www_form_component(m["user"]) +
146
+ ':' +
147
+ URI.encode_www_form_component(m["password"]) +
148
+ m["path"]
149
+ else
150
+ host_str
151
+ end
152
+ end
153
+
154
+ def get_connection_options(con_host=nil)
155
+
156
+ hosts = if con_host || @hosts
157
+ (con_host || @hosts).split(',').map do |host_str|
158
+ # Support legacy hosts format host:port,host:port,host:port...
159
+ if host_str.match(%r{^[^:]+(\:\d+)?$})
160
+ {
161
+ host: host_str.split(':')[0],
162
+ port: (host_str.split(':')[1] || @port).to_i,
163
+ scheme: @scheme.to_s
164
+ }
165
+ else
166
+ # New hosts format expects URLs such as http://logs.foo.com,https://john:pass@logs2.foo.com/elastic
167
+ uri = URI(get_escaped_userinfo(host_str))
168
+ %w(user password path).inject(host: uri.host, port: uri.port, scheme: uri.scheme) do |hash, key|
169
+ hash[key.to_sym] = uri.public_send(key) unless uri.public_send(key).nil? || uri.public_send(key) == ''
170
+ hash
171
+ end
172
+ end
173
+ end.compact
174
+ else
175
+ [{host: @host, port: @port, scheme: @scheme.to_s}]
176
+ end.each do |host|
177
+ host.merge!(user: @user, password: @password) if !host[:user] && @user
178
+ host.merge!(path: @path) if !host[:path] && @path
179
+ end
180
+
181
+ {
182
+ hosts: hosts
183
+ }
184
+ end
185
+
186
+ def emit_error_label_event(&block)
187
+ # If `emit_error_label_event` is specified as false, error event emittions are not occurred.
188
+ if emit_error_label_event
189
+ block.call
190
+ end
191
+ end
192
+
193
+ def start
194
+ super
195
+
196
+ timer_execute(:in_opensearch_timer, @interval, repeat: @repeat, &method(:run))
197
+ end
198
+
199
+ # We might be able to use
200
+ # Fluent::Parser::TimeParser, but it doesn't quite do what we want - if gives
201
+ # [sec,nsec] where as we want something we can call `strftime` on...
202
+ def create_time_parser
203
+ if @timestamp_key_format
204
+ begin
205
+ # Strptime doesn't support all formats, but for those it does it's
206
+ # blazingly fast.
207
+ strptime = Strptime.new(@timestamp_key_format)
208
+ Proc.new { |value|
209
+ value = convert_numeric_time_into_string(value, @timestamp_key_format) if value.is_a?(Numeric)
210
+ strptime.exec(value).to_time
211
+ }
212
+ rescue
213
+ # Can happen if Strptime doesn't recognize the format; or
214
+ # if strptime couldn't be required (because it's not installed -- it's
215
+ # ruby 2 only)
216
+ Proc.new { |value|
217
+ value = convert_numeric_time_into_string(value, @timestamp_key_format) if value.is_a?(Numeric)
218
+ DateTime.strptime(value, @timestamp_key_format).to_time
219
+ }
220
+ end
221
+ else
222
+ Proc.new { |value|
223
+ value = convert_numeric_time_into_string(value) if value.is_a?(Numeric)
224
+ DateTime.parse(value).to_time
225
+ }
226
+ end
227
+ end
228
+
229
+ def convert_numeric_time_into_string(numeric_time, timestamp_key_format = "%Y-%m-%dT%H:%M:%S.%N%z")
230
+ numeric_time_parser = Fluent::NumericTimeParser.new(:float)
231
+ Time.at(numeric_time_parser.parse(numeric_time).to_r).strftime(timestamp_key_format)
232
+ end
233
+
234
+ def parse_time(value, event_time, tag)
235
+ @timestamp_parser.call(value)
236
+ rescue => e
237
+ emit_error_label_event do
238
+ router.emit_error_event(@timestamp_parse_error_tag, Fluent::Engine.now, {'tag' => tag, 'time' => event_time, 'format' => @timestamp_key_format, 'value' => value}, e)
239
+ end
240
+ return Time.at(event_time).to_time
241
+ end
242
+
243
+ def client(host = nil)
244
+ # check here to see if we already have a client connection for the given host
245
+ connection_options = get_connection_options(host)
246
+
247
+ @_os = nil unless is_existing_connection(connection_options[:hosts])
248
+
249
+ @_os ||= begin
250
+ @current_config = connection_options[:hosts].clone
251
+ adapter_conf = lambda {|f| f.adapter @http_backend, @backend_options }
252
+ local_reload_connections = @reload_connections
253
+ if local_reload_connections && @reload_after > DEFAULT_RELOAD_AFTER
254
+ local_reload_connections = @reload_after
255
+ end
256
+
257
+ headers = { 'Content-Type' => "application/json" }.merge(@custom_headers)
258
+
259
+ transport = OpenSearch::Transport::Transport::HTTP::Faraday.new(
260
+ connection_options.merge(
261
+ options: {
262
+ reload_connections: local_reload_connections,
263
+ reload_on_failure: @reload_on_failure,
264
+ resurrect_after: @resurrect_after,
265
+ logger: @transport_logger,
266
+ transport_options: {
267
+ headers: headers,
268
+ request: { timeout: @request_timeout },
269
+ ssl: { verify: @ssl_verify, ca_file: @ca_file, version: @ssl_version }
270
+ },
271
+ http: {
272
+ user: @user,
273
+ password: @password
274
+ },
275
+ sniffer_class: @sniffer_class,
276
+ }), &adapter_conf)
277
+ OpenSearch::Client.new transport: transport
278
+ end
279
+ end
280
+
281
+ def is_existing_connection(host)
282
+ # check if the host provided match the current connection
283
+ return false if @_os.nil?
284
+ return false if @current_config.nil?
285
+ return false if host.length != @current_config.length
286
+
287
+ for i in 0...host.length
288
+ if !host[i][:host].eql? @current_config[i][:host] || host[i][:port] != @current_config[i][:port]
289
+ return false
290
+ end
291
+ end
292
+
293
+ return true
294
+ end
295
+
296
+ def run
297
+ return run_slice if @num_slices <= 1
298
+
299
+ log.warn("Large slice number is specified:(#{@num_slices}). Consider reducing num_slices") if @num_slices > 8
300
+
301
+ @num_slices.times.map do |slice_id|
302
+ thread_create(:"in_opensearch_thread_#{slice_id}") do
303
+ run_slice(slice_id)
304
+ end
305
+ end
306
+ rescue Faraday::ConnectionFailed => e
307
+ log.warn "Connection to OpenSearch failed during search in the 'run' method: #{e.message}. Retrying..."
308
+ retry
309
+ end
310
+
311
+ def run_slice(slice_id=nil)
312
+ client.ping
313
+ # slice_query = @base_query
314
+ # slice_query = slice_query.merge('slice' => { 'id' => slice_id, 'max' => @num_slices}) unless slice_id.nil?
315
+ # result = client.search(@options.merge(:body => Yajl.dump(slice_query) ))
316
+ # es = Fluent::MultiEventStream.new
317
+
318
+ # result["hits"]["hits"].each {|hit| process_events(hit, es)}
319
+ # has_hits = result['hits']['hits'].any?
320
+ # scroll_id = result['_scroll_id']
321
+
322
+ # while has_hits && scroll_id
323
+ # result = process_next_scroll_request(es, scroll_id)
324
+ # has_hits = result['has_hits']
325
+ # scroll_id = result['_scroll_id']
326
+ # end
327
+
328
+ # router.emit_stream(@tag, es)
329
+ # clear_scroll(scroll_id)
330
+ end
331
+
332
+ def clear_scroll(scroll_id)
333
+ client.clear_scroll(scroll_id: scroll_id) if scroll_id
334
+ rescue => e
335
+ # ignore & log any clear_scroll errors
336
+ log.warn("Ignoring clear_scroll exception", message: e.message, exception: e.class)
337
+ end
338
+
339
+ def process_scroll_request(scroll_id)
340
+ client.scroll(:body => { :scroll_id => scroll_id }, :scroll => @scroll)
341
+ end
342
+
343
+ def process_next_scroll_request(es, scroll_id)
344
+ result = process_scroll_request(scroll_id)
345
+ result['hits']['hits'].each { |hit| process_events(hit, es) }
346
+ {'has_hits' => result['hits']['hits'].any?, '_scroll_id' => result['_scroll_id']}
347
+ end
348
+
349
+ def process_events(hit, es)
350
+ event = hit["_source"]
351
+ time = Fluent::Engine.now
352
+ if @parse_timestamp
353
+ if event.has_key?(TIMESTAMP_FIELD)
354
+ rts = event[TIMESTAMP_FIELD]
355
+ time = parse_time(rts, time, @tag)
356
+ end
357
+ end
358
+ if @docinfo
359
+ docinfo_target = event[@docinfo_target] || {}
360
+
361
+ unless docinfo_target.is_a?(Hash)
362
+ raise UnrecoverableError, "incompatible type for the docinfo_target=#{@docinfo_target} field in the `_source` document, expected a hash got:", :type => docinfo_target.class, :event => event
363
+ end
364
+
365
+ @docinfo_fields.each do |field|
366
+ docinfo_target[field] = hit[field]
367
+ end
368
+
369
+ event[@docinfo_target] = docinfo_target
370
+ end
371
+ es.add(time, event)
372
+ end
373
+ end
374
+ end
@@ -0,0 +1,48 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # The fluent-plugin-opensearch Contributors require contributions made to
4
+ # this file be licensed under the Apache-2.0 license or a
5
+ # compatible open source license.
6
+ #
7
+ # Modifications Copyright fluent-plugin-opensearch Contributors. See
8
+ # GitHub history for details.
9
+ #
10
+ # Licensed to Uken Inc. under one or more contributor
11
+ # license agreements. See the NOTICE file distributed with
12
+ # this work for additional information regarding copyright
13
+ # ownership. Uken Inc. licenses this file to you under
14
+ # the Apache License, Version 2.0 (the "License"); you may
15
+ # not use this file except in compliance with the License.
16
+ # You may obtain a copy of the License at
17
+ #
18
+ # http://www.apache.org/licenses/LICENSE-2.0
19
+ #
20
+ # Unless required by applicable law or agreed to in writing,
21
+ # software distributed under the License is distributed on an
22
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23
+ # KIND, either express or implied. See the License for the
24
+ # specific language governing permissions and limitations
25
+ # under the License.
26
+
27
+ require 'oj'
28
+
29
+ module Fluent::Plugin
30
+ module Serializer
31
+
32
+ class Oj
33
+ include OpenSearch::Transport::Transport::Serializer::Base
34
+
35
+ # De-serialize a Hash from JSON string
36
+ #
37
+ def load(string, options={})
38
+ ::Oj.load(string, options)
39
+ end
40
+
41
+ # Serialize a Hash to JSON string
42
+ #
43
+ def dump(object, options={})
44
+ ::Oj.dump(object, options)
45
+ end
46
+ end
47
+ end
48
+ end