fluent-plugin-opensearch 1.1.4 → 1.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 578e5c63061d5f1cb3f3729dee4ea630564dc60d82f6b047c8e4b23830fdf16a
4
- data.tar.gz: a502d46eb5a3a59c5c4f65f9009623ff32fde1de4cfda7e181437a8720bdbda1
3
+ metadata.gz: d0a251b65daac387b7520c49475a881440181bbe44da46849d8bdee61b9a4374
4
+ data.tar.gz: ec313c1dba6a723a4c5fd4cf672abaf7e1be9785e29176c31f419b253949f539
5
5
  SHA512:
6
- metadata.gz: 77b3373041396440cf8debaea8fd2ab95b94df950cbfe6105f65d45cce7092a6f89bd019b38d4022051c651541d3d21918625ea1b9939f58108101eae90a027f
7
- data.tar.gz: 2e746cd797f5d24f08dbb386ea42b9bff506831c56f9f4b92dcf99dde321b9b613c7473cbad39bd1f6d338ae6dd094a87f838141306111ab49b5a76886346918
6
+ metadata.gz: 1e40ac1b0816b42fa6d84a3cfd72eb3219ce688a80e15e110670e32a6ee7dd97a20542dd6d056a443f92d173e16f3a29b614a249e64ba2babb18243d3ad4ba7f
7
+ data.tar.gz: 342f988468e7fb9a47ef2111e88a474b9d7a97d310b3112173e1ca7dc510dea3c1a0ddf6f0c7515c5769e389a01a68f761540576e0856f718e06e2e83ab0f712
@@ -5,7 +5,7 @@ about: Suggest an idea for this project
5
5
  ---
6
6
 
7
7
  (check apply)
8
- - [ ] read [the contribution guideline](https://github.com/uken/fluent-plugin-elasticsearch/blob/master/CONTRIBUTING.md)
8
+ - [ ] read [the contribution guideline](https://github.com/fluent/fluent-plugin-opensearch/blob/master/CONTRIBUTING.md)
9
9
 
10
10
  **Is your feature request related to a problem? Please describe.**
11
11
 
@@ -22,5 +22,6 @@ jobs:
22
22
  CI: true
23
23
  run: |
24
24
  gem install bundler rake
25
+ bundle config set path 'vendor/bundle'
25
26
  bundle install --jobs 4 --retry 3
26
27
  bundle exec rake test
data/History.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ### [Unreleased]
4
4
 
5
+ ### 1.1.5
6
+ - Fix bundle installation error on GitHub Action (#143)
7
+ - Prevent AWS credentials refresh from stopping on exception (#142)
8
+ - Added a retry logic and a service availability check function for high availability. (#136)
9
+ - out\_opensearch\_data\_stream: Early return on empty body (#131)
10
+ - Docs/cleanup issue templates (#119)
11
+ - pin dependency opensearch-ruby to 3.0.1 (#116)
12
+
5
13
  ### 1.1.4
6
14
  - test: remove minitest to correct misjudge of the framework by flexmock (#114)
7
15
  - Add logic to write method of out_opensearch_data_stream (#109)
@@ -24,6 +24,16 @@
24
24
  + [docinfo_fields](#docinfo_fields)
25
25
  + [docinfo_target](#docinfo_target)
26
26
  + [docinfo](#docinfo)
27
+ + [check_connection](#check_connection)
28
+ + [retry_forever](#retry_forever)
29
+ + [retry_timeout](#retry_timeout)
30
+ + [retry_max_times](#retry_max_times)
31
+ + [retry_type](#retry_type)
32
+ + [retry_wait](#retry_wait)
33
+ + [retry_exponential_backoff_base](#retry_exponential_backoff_base)
34
+ + [retry_max_interval](#retry_max_interval)
35
+ + [retry_randomize](#retry_randomize)
36
+
27
37
  * [Advanced Usage](#advanced-usage)
28
38
 
29
39
  ## Usage
@@ -274,6 +284,88 @@ This parameter specifies whether docinfo information including or not. The defau
274
284
  docinfo false
275
285
  ```
276
286
 
287
+ ### check_connection
288
+
289
+ The parameter for checking on connection availability with Elasticsearch or Opensearch hosts. The default value is `true`.
290
+
291
+ ```
292
+ check_connection true
293
+ ```
294
+ ### retry_forever
295
+
296
+ The parameter If true, plugin will ignore retry_timeout and retry_max_times options and retry forever. The default value is `true`.
297
+
298
+ ```
299
+ retry_forever true
300
+ ```
301
+
302
+ ### retry_timeout
303
+
304
+ The parameter maximum time (seconds) to retry again the failed try, until the plugin discards the retry.
305
+ If the next retry is going to exceed this time limit, the last retry will be made at exactly this time limit..
306
+ The default value is `72h`.
307
+ 72hours == 17 times with exponential backoff (not to change default behavior)
308
+
309
+ ```
310
+ retry_timeout 72 * 60 * 60
311
+ ```
312
+
313
+ ### retry_max_times
314
+
315
+ The parameter maximum number of times to retry the failed try. The default value is `5`
316
+
317
+ ```
318
+ retry_max_times 5
319
+ ```
320
+
321
+ ### retry_type
322
+
323
+ The parameter needs for how long need to wait (time in seconds) to retry again:
324
+ `exponential_backoff`: wait in seconds will become large exponentially per failure,
325
+ `periodic`: plugin will retry periodically with fixed intervals (configured via retry_wait). The default value is `:exponential_backoff`
326
+ Periodic -> fixed :retry_wait
327
+ Exponential backoff: k is number of retry times
328
+ c: constant factor, @retry_wait
329
+ b: base factor, @retry_exponential_backoff_base
330
+ k: times
331
+ total retry time: c + c * b^1 + (...) + c*b^k = c*b^(k+1) - 1
332
+
333
+ ```
334
+ retry_type exponential_backoff
335
+ ```
336
+
337
+ ### retry_wait
338
+
339
+ The parameter needs for wait in seconds before the next retry to again or constant factor of exponential backoff. The default value is `5`
340
+
341
+ ```
342
+ retry_wait 5
343
+ ```
344
+
345
+ ### retry_exponential_backoff_base
346
+
347
+ The parameter The base number of exponential backoff for retries. The default value is `2`
348
+
349
+ ```
350
+ retry_exponential_backoff_base 2
351
+ ```
352
+
353
+ ### retry_max_interval
354
+
355
+ The parameter maximum interval (seconds) for exponential backoff between retries while failing. The default value is `nil`
356
+
357
+ ```
358
+ retry_max_interval nil
359
+ ```
360
+
361
+ ### retry_randomize
362
+
363
+ The parameter If true, the plugin will retry after randomized interval not to do burst retries. The default value is `false`
364
+
365
+ ```
366
+ retry_randomize false
367
+ ```
368
+
277
369
  ## Advanced Usage
278
370
 
279
371
  OpenSearch Input plugin and OpenSearch output plugin can combine to transfer records into another cluster.
@@ -3,7 +3,7 @@ $:.push File.expand_path('../lib', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = 'fluent-plugin-opensearch'
6
- s.version = '1.1.4'
6
+ s.version = '1.1.5'
7
7
  s.authors = ['Hiroshi Hatake']
8
8
  s.email = ['cosmo0920.wp@gmail.com']
9
9
  s.description = %q{Opensearch output plugin for Fluent event collector}
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
23
23
  s.required_ruby_version = Gem::Requirement.new(">= 2.3".freeze)
24
24
 
25
25
  s.add_runtime_dependency 'fluentd', '>= 0.14.22'
26
- s.add_runtime_dependency 'opensearch-ruby'
26
+ s.add_runtime_dependency 'opensearch-ruby', '>= 3.0.1'
27
27
  s.add_runtime_dependency "aws-sdk-core", "~> 3"
28
28
  s.add_runtime_dependency 'excon', '>= 0'
29
29
  s.add_runtime_dependency 'faraday', '>= 2.0.0'
@@ -32,7 +32,7 @@ Gem::Specification.new do |s|
32
32
 
33
33
  s.add_development_dependency 'rake', '>= 0'
34
34
  s.add_development_dependency 'webrick', '~> 1.7.0'
35
- s.add_development_dependency 'webmock', '~> 3'
35
+ s.add_development_dependency 'webmock', '~> 3.18.1'
36
36
  s.add_development_dependency 'test-unit', '~> 3.3.0'
37
37
  s.add_development_dependency 'minitest', '~> 5.8'
38
38
  s.add_development_dependency 'flexmock', '~> 2.0'
@@ -29,6 +29,7 @@ require 'opensearch'
29
29
  require 'faraday/excon'
30
30
  require 'fluent/log-ext'
31
31
  require 'fluent/plugin/input'
32
+ require 'fluent/plugin_helper'
32
33
  require_relative 'opensearch_constants'
33
34
 
34
35
  module Fluent::Plugin
@@ -39,7 +40,7 @@ module Fluent::Plugin
39
40
  DEFAULT_STORAGE_TYPE = 'local'
40
41
  METADATA = "@metadata".freeze
41
42
 
42
- helpers :timer, :thread
43
+ helpers :timer, :thread, :retry_state
43
44
 
44
45
  Fluent::Plugin.register_input('opensearch', self)
45
46
 
@@ -80,6 +81,23 @@ module Fluent::Plugin
80
81
  config_param :docinfo_fields, :array, :default => ['_index', '_type', '_id']
81
82
  config_param :docinfo_target, :string, :default => METADATA
82
83
  config_param :docinfo, :bool, :default => false
84
+ config_param :check_connection, :bool, :default => true
85
+ config_param :retry_forever, :bool, default: true, desc: 'If true, plugin will ignore retry_timeout and retry_max_times options and retry forever.'
86
+ config_param :retry_timeout, :time, default: 72 * 60 * 60, desc: 'The maximum seconds to retry'
87
+ # 72hours == 17 times with exponential backoff (not to change default behavior)
88
+ config_param :retry_max_times, :integer, default: 5, desc: 'The maximum number of times to retry'
89
+ # exponential backoff sequence will be initialized at the time of this threshold
90
+ config_param :retry_type, :enum, list: [:exponential_backoff, :periodic], default: :exponential_backoff
91
+ ### Periodic -> fixed :retry_wait
92
+ ### Exponential backoff: k is number of retry times
93
+ # c: constant factor, @retry_wait
94
+ # b: base factor, @retry_exponential_backoff_base
95
+ # k: times
96
+ # total retry time: c + c * b^1 + (...) + c*b^k = c*b^(k+1) - 1
97
+ config_param :retry_wait, :time, default: 5, desc: 'Seconds to wait before next retry , or constant factor of exponential backoff.'
98
+ config_param :retry_exponential_backoff_base, :float, default: 2, desc: 'The base number of exponential backoff for retries.'
99
+ config_param :retry_max_interval, :time, default: nil, desc: 'The maximum interval seconds for exponential backoff between retries while failing.'
100
+ config_param :retry_randomize, :bool, default: false, desc: 'If true, output plugin will retry after randomized interval not to do burst retries.'
83
101
 
84
102
  include Fluent::Plugin::OpenSearchConstants
85
103
 
@@ -92,6 +110,7 @@ module Fluent::Plugin
92
110
 
93
111
  @timestamp_parser = create_time_parser
94
112
  @backend_options = backend_options
113
+ @retry = nil
95
114
 
96
115
  raise Fluent::ConfigError, "`password` must be present if `user` is present" if @user && @password.nil?
97
116
 
@@ -138,6 +157,15 @@ module Fluent::Plugin
138
157
  raise Fluent::ConfigError, "You must install #{@http_backend} gem. Exception: #{ex}"
139
158
  end
140
159
 
160
+ def retry_state(randomize)
161
+ retry_state_create(
162
+ :input_retries, @retry_type, @retry_wait, @retry_timeout,
163
+ forever: @retry_forever, max_steps: @retry_max_times,
164
+ max_interval: @retry_max_interval, backoff_base: @retry_exponential_backoff_base,
165
+ randomize: randomize
166
+ )
167
+ end
168
+
141
169
  def get_escaped_userinfo(host_str)
142
170
  if m = host_str.match(/(?<scheme>.*)%{(?<user>.*)}:%{(?<password>.*)}(?<path>@.*)/)
143
171
  m["scheme"] +
@@ -176,12 +204,29 @@ module Fluent::Plugin
176
204
  host.merge!(user: @user, password: @password) if !host[:user] && @user
177
205
  host.merge!(path: @path) if !host[:path] && @path
178
206
  end
179
-
207
+ live_hosts = @check_connection ? hosts.select { |host| reachable_host?(host) } : hosts
180
208
  {
181
- hosts: hosts
209
+ hosts: live_hosts
182
210
  }
183
211
  end
184
212
 
213
+ def reachable_host?(host)
214
+ client = OpenSearch::Client.new(
215
+ host: ["#{host[:scheme]}://#{host[:host]}:#{host[:port]}"],
216
+ user: host[:user],
217
+ password: host[:password],
218
+ reload_connections: @reload_connections,
219
+ request_timeout: @request_timeout,
220
+ resurrect_after: @resurrect_after,
221
+ reload_on_failure: @reload_on_failure,
222
+ transport_options: { ssl: { verify: @ssl_verify, ca_file: @ca_file, version: @ssl_version } }
223
+ )
224
+ client.ping
225
+ rescue => e
226
+ log.warn "Failed to connect to #{host[:scheme]}://#{host[:host]}:#{host[:port]}: #{e.message}"
227
+ false
228
+ end
229
+
185
230
  def emit_error_label_event(&block)
186
231
  # If `emit_error_label_event` is specified as false, error event emittions are not occurred.
187
232
  if emit_error_label_event
@@ -292,6 +337,25 @@ module Fluent::Plugin
292
337
  return true
293
338
  end
294
339
 
340
+ def update_retry_state(error=nil)
341
+ if error
342
+ unless @retry
343
+ @retry = retry_state(@retry_randomize)
344
+ end
345
+ @retry.step
346
+ #Raise error if the retry limit has been reached
347
+ raise "Hit limit for retries. retry_times: #{@retry.steps}, error: #{error.message}" if @retry.limit?
348
+ #Retry if the limit hasn't been reached
349
+ log.warn("failed to connect or search.", retry_times: @retry.steps, next_retry_time: @retry.next_time.round, error: error.message)
350
+ sleep(@retry.next_time - Time.now)
351
+ else
352
+ unless @retry.nil?
353
+ log.debug("retry succeeded.")
354
+ @retry = nil
355
+ end
356
+ end
357
+ end
358
+
295
359
  def run
296
360
  return run_slice if @num_slices <= 1
297
361
 
@@ -302,6 +366,9 @@ module Fluent::Plugin
302
366
  run_slice(slice_id)
303
367
  end
304
368
  end
369
+ rescue Faraday::ConnectionFailed, OpenSearch::Transport::Transport::Error => error
370
+ update_retry_state(error)
371
+ retry
305
372
  end
306
373
 
307
374
  def run_slice(slice_id=nil)
@@ -321,7 +388,15 @@ module Fluent::Plugin
321
388
  end
322
389
 
323
390
  router.emit_stream(@tag, es)
391
+ clear_scroll(scroll_id)
392
+ update_retry_state
393
+ end
394
+
395
+ def clear_scroll(scroll_id)
324
396
  client.clear_scroll(scroll_id: scroll_id) if scroll_id
397
+ rescue => e
398
+ # ignore & log any clear_scroll errors
399
+ log.warn("Ignoring clear_scroll exception", message: e.message, exception: e.class)
325
400
  end
326
401
 
327
402
  def process_scroll_request(scroll_id)
@@ -350,7 +350,11 @@ module Fluent::Plugin
350
350
 
351
351
  @credential_mutex.synchronize do
352
352
  @_os = nil
353
- @_aws_credentials = aws_credentials(@endpoint)
353
+ begin
354
+ @_aws_credentials = aws_credentials(@endpoint)
355
+ rescue => e
356
+ log.error("Failed to get new AWS credentials: #{e}")
357
+ end
354
358
  end
355
359
  end
356
360
  end
@@ -201,6 +201,8 @@ module Fluent::Plugin
201
201
  end
202
202
  end
203
203
 
204
+ return if bulk_message.to_s.empty?
205
+
204
206
  params = {
205
207
  index: data_stream_name,
206
208
  body: bulk_message
@@ -39,6 +39,7 @@ class OpenSearchInputTest < Test::Unit::TestCase
39
39
  CONFIG = %[
40
40
  tag raw.opensearch
41
41
  interval 2
42
+ check_connection false
42
43
  ]
43
44
 
44
45
  def setup
@@ -190,6 +191,7 @@ class OpenSearchInputTest < Test::Unit::TestCase
190
191
  user john
191
192
  password doe
192
193
  tag raw.opensearch
194
+ check_connection false
193
195
  }
194
196
  instance = driver(config).instance
195
197
 
@@ -228,6 +230,7 @@ class OpenSearchInputTest < Test::Unit::TestCase
228
230
  user john
229
231
  password doe
230
232
  tag raw.opensearch
233
+ check_connection false
231
234
  }
232
235
  instance = driver(config).instance
233
236
 
@@ -249,6 +252,7 @@ class OpenSearchInputTest < Test::Unit::TestCase
249
252
  user %{j+hn}
250
253
  password %{d@e}
251
254
  tag raw.opensearch
255
+ check_connection false
252
256
  }
253
257
  instance = driver(config).instance
254
258
 
@@ -271,6 +275,7 @@ class OpenSearchInputTest < Test::Unit::TestCase
271
275
  path /es/
272
276
  port 123
273
277
  tag raw.opensearch
278
+ check_connection false
274
279
  }
275
280
  instance = driver(config).instance
276
281
 
@@ -295,6 +300,7 @@ class OpenSearchInputTest < Test::Unit::TestCase
295
300
  user default_user
296
301
  password default_password
297
302
  tag raw.opensearch
303
+ check_connection false
298
304
  }
299
305
  instance = driver(config).instance
300
306
 
@@ -323,6 +329,7 @@ class OpenSearchInputTest < Test::Unit::TestCase
323
329
  user default_user
324
330
  password default_password
325
331
  tag raw.opensearch
332
+ check_connection false
326
333
  }
327
334
  instance = driver(config).instance
328
335
 
@@ -3977,4 +3977,26 @@ class OpenSearchOutputTest < Test::Unit::TestCase
3977
3977
  end
3978
3978
  }
3979
3979
  end
3980
+
3981
+ def test_no_aws_credentials_refresh_exception
3982
+ # See https://github.com/fluent/fluent-plugin-opensearch/issues/129
3983
+ endpoint_config =
3984
+ Fluent::Config::Element.new('endpoint', '', {
3985
+ 'url' => "https://search-opensearch.aws.example.com/",
3986
+ 'region' => "local",
3987
+ 'access_key_id' => 'YOUR_AWESOME_KEY',
3988
+ 'secret_access_key' => 'YOUR_AWESOME_SECRET',
3989
+ 'refresh_credentials_interval' => '0',
3990
+ }, [])
3991
+ config = Fluent::Config::Element.new('ROOT', '**', { '@type' => 'opensearch' },
3992
+ [endpoint_config])
3993
+ # aws_credentials will be called twice in
3994
+ # OpenSearchOutput#configure call, and in the 2nd call was changed not
3995
+ # to emit exception. (instead, logging error) so check the error logs
3996
+ flexmock(Fluent::Plugin::OpenSearchOutput).new_instances.should_receive(:aws_credentials)
3997
+ .and_return(true).and_raise(::RuntimeError.new("No valid AWS credentials found."))
3998
+ d = driver(config)
3999
+ d.run
4000
+ assert { d.logs.any?(/\[error\]: Failed to get new AWS credentials: No valid AWS credentials found.\n/) }
4001
+ end
3980
4002
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-opensearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 1.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hiroshi Hatake
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-21 00:00:00.000000000 Z
11
+ date: 2024-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 3.0.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 3.0.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: aws-sdk-core
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -142,14 +142,14 @@ dependencies:
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: '3'
145
+ version: 3.18.1
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: '3'
152
+ version: 3.18.1
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: test-unit
155
155
  requirement: !ruby/object:Gem::Requirement
@@ -203,6 +203,7 @@ files:
203
203
  - ".editorconfig"
204
204
  - ".github/ISSUE_TEMPLATE/bug_report.md"
205
205
  - ".github/ISSUE_TEMPLATE/feature_request.md"
206
+ - ".github/PULL_REQUEST_TEMPLATE/pull_request_template.md"
206
207
  - ".github/workflows/coverage.yaml"
207
208
  - ".github/workflows/issue-auto-closer.yml"
208
209
  - ".github/workflows/linux.yml"
@@ -212,9 +213,7 @@ files:
212
213
  - CONTRIBUTING.md
213
214
  - Gemfile
214
215
  - History.md
215
- - ISSUE_TEMPLATE.md
216
216
  - LICENSE.txt
217
- - PULL_REQUEST_TEMPLATE.md
218
217
  - README.OpenSearchGenID.md
219
218
  - README.OpenSearchInput.md
220
219
  - README.Troubleshooting.md
data/ISSUE_TEMPLATE.md DELETED
@@ -1,26 +0,0 @@
1
- (check apply)
2
- - [ ] read [the contribution guideline](https://github.com/fluent/fluent-plugin-opensearch/blob/master/CONTRIBUTING.md)
3
-
4
- #### Problem
5
-
6
- ...
7
-
8
- #### Steps to replicate
9
-
10
- Provide example config and message
11
-
12
- #### Expected Behavior or What you need to ask
13
-
14
- ...
15
-
16
- #### Using Fluentd and OpenSearch plugin versions
17
-
18
- * OS version
19
- * Bare Metal or within Docker or Kubernetes or others?
20
- * Fluentd v1.0 or later
21
- * paste result of ``fluentd --version`` or ``td-agent --version``
22
- * OpenSearch plugin version
23
- * paste boot log of fluentd or td-agent
24
- * paste result of ``fluent-gem list``, ``td-agent-gem list`` or your Gemfile.lock
25
- * OpenSearch version (optional)
26
- * OpenSearch template(s) (optional)