fluent-plugin-s3 1.7.2 → 1.8.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1450176cd690d79412b7522ce8e67163c2f3c9c1f87941e57d6b3f3410bdf330
4
- data.tar.gz: ee66229b8cd65ef2507feb5229d54322ed2f076e2daefb3f32e3b0e2ba47c634
3
+ metadata.gz: 4ad2143d493bcb5b5805df10225cdb068bffc8ae24a1a6838bde343b18a845ed
4
+ data.tar.gz: faf67f2a0e65e73b385944113a485f509365b3b9f1e8059ef1b06df8c6719607
5
5
  SHA512:
6
- metadata.gz: f0428c1da0ef734a65d86aec878374636cf4bfd10ed2ab06578d3d8a9cc8ee607e62b4a85c05144599dec2167995dbd836b70cb7cfb66f38fc38d5e82dc1073a
7
- data.tar.gz: bb4d7820ff8fa7fa3e58092b69fc78bb4804d92854206fb61c4efa21b415519eff682cf64763a9ec237f30f8997ab20b0b395bb8f1f17a8c88a9c63b3381b02b
6
+ metadata.gz: 51c6cd59240d0d2055260b779e9887ab5a8fc0839246cf9154566271de72e30a73ba0587ae2945dd275c1b16b71c43e165a77e43419e73279fbb938904790a48
7
+ data.tar.gz: ff8aa7a1c32cf273527165eee2dec5df4df217d20e5047c53908469bf13fd8baf6b803781e30d09126d83e0ba4658bf8b28ae2b034e403f627ae9a9e98b312a3
@@ -1,5 +1,6 @@
1
1
  name: Bug Report
2
2
  description: Create a report with a procedure for reproducing the bug
3
+ labels: "waiting-for-triage"
3
4
  body:
4
5
  - type: markdown
5
6
  attributes:
@@ -1,5 +1,6 @@
1
1
  name: Feature request
2
2
  description: Suggest an idea for this project
3
+ labels: "waiting-for-triage"
3
4
  body:
4
5
  - type: markdown
5
6
  attributes:
@@ -0,0 +1,23 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: 'github-actions'
4
+ directory: '/'
5
+ schedule:
6
+ interval: 'monthly'
7
+ groups:
8
+ # PR: "Security update [package] from [old] to [new]"
9
+ # This PR should be merged in hurry
10
+ security-updates:
11
+ applies-to: security-updates
12
+ patterns:
13
+ - '*'
14
+
15
+ # PR: "Bump [package] from [old] to [new]"
16
+ # No need to be merged this PR in hurry. It is enough to merge
17
+ # once in a month.
18
+ monthly-updates:
19
+ applies-to: version-updates
20
+ patterns:
21
+ - '*'
22
+ # Allow to create PR both of security and normal updates.
23
+ open-pull-requests-limit: 1
@@ -4,25 +4,34 @@ on:
4
4
  branches: [master]
5
5
  pull_request:
6
6
  branches: [master]
7
+ schedule:
8
+ - cron: '0 0 1 * *'
7
9
  jobs:
10
+ ruby-versions:
11
+ uses: ruby/actions/.github/workflows/ruby_versions.yml@master
12
+ with:
13
+ engine: cruby
14
+ min_version: 2.7
8
15
  build:
16
+ needs: ruby-versions
9
17
  runs-on: ${{ matrix.os }}
10
18
  strategy:
11
19
  fail-fast: false
12
20
  matrix:
13
- ruby: [ '3.1', '3.0', '2.7' ]
21
+ ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
22
+ exclude:
23
+ - ruby: head
14
24
  os:
15
25
  - ubuntu-latest
16
26
  name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }}
17
27
  steps:
18
- - uses: actions/checkout@v2
19
- - uses: ruby/setup-ruby@v1
28
+ - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
29
+ - uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f # v1.310.0
20
30
  with:
21
31
  ruby-version: ${{ matrix.ruby }}
22
32
  - name: unit testing
23
33
  env:
24
34
  CI: true
25
35
  run: |
26
- gem install bundler rake
27
36
  bundle install --jobs 4 --retry 3
28
37
  bundle exec rake test
@@ -6,17 +6,22 @@ on:
6
6
  jobs:
7
7
  stale:
8
8
  runs-on: ubuntu-latest
9
+ permissions:
10
+ issues: write
11
+ pull-requests: write
9
12
  steps:
10
- - uses: actions/stale@v3
13
+ - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
11
14
  with:
12
15
  repo-token: ${{ secrets.GITHUB_TOKEN }}
13
- days-before-stale: 90
14
- days-before-close: 30
15
- stale-issue-message: "This issue has been automatically marked as stale because it has been open 90 days with no activity. Remove stale label or comment or this issue will be closed in 30 days"
16
- stale-pr-message: "This PR has been automatically marked as stale because it has been open 90 days with no activity. Remove stale label or comment or this PR will be closed in 30 days"
17
- close-issue-message: "This issue was automatically closed because of stale in 30 days"
18
- close-pr-message: "This PR was automatically closed because of stale in 30 days"
16
+ days-before-stale: 30
17
+ days-before-close: 7
18
+ stale-issue-message: "This issue has been automatically marked as stale because it has been open 30 days with no activity. Remove stale label or comment or this issue will be closed in 7 days"
19
+ stale-pr-message: "This PR has been automatically marked as stale because it has been open 30 days with no activity. Remove stale label or comment or this PR will be closed in 7 days"
20
+ close-issue-message: "This issue was automatically closed because of stale in 7 days"
21
+ close-pr-message: "This PR was automatically closed because of stale in 7 days"
19
22
  stale-pr-label: "stale"
20
23
  stale-issue-label: "stale"
21
- exempt-issue-labels: "bug,help wanted,enhancement"
22
- exempt-pr-labels: "bug,help wanted,enhancement"
24
+ exempt-issue-labels: "waiting-for-triage,bug,help wanted,enhancement"
25
+ exempt-pr-labels: "waiting-for-triage,bug,help wanted,enhancement"
26
+ exempt-all-assignees: true
27
+ exempt-all-milestones: true
data/ChangeLog CHANGED
@@ -1,3 +1,33 @@
1
+ Release 1.8.5 - 2026/06/25
2
+
3
+ * in_s3: enforce size limits on decompressed payloads
4
+
5
+ Release 1.8.4 - 2026/03/04
6
+
7
+ * in_s3: add aws_profile / aws_credential_process parameters for credencials (GitHub: #464)
8
+ * out_s3: add aws_profile / aws_credential_process parameters for credencials (GitHub: #464)
9
+ * out_s3: stop unnecessary re-compression when chunk is already compressed (GitHub: #460)
10
+ * gem: use same zstd-ruby version with Fluentd (GitHub: #463)
11
+ * gem: add ostruct gem as dependency for Ruby 4.0 (GitHub: #461)
12
+ * docs: add missing zstd compression (GitHub: #459)
13
+
14
+ Release 1.8.3 - 2025/02/18
15
+
16
+ * out_s3: Add `sts_http_proxy` and `sts_endpoint_url` to web_identity_credentials (GitHub: #452)
17
+
18
+ Release 1.8.2 - 2024/12/18
19
+
20
+ * out_s3: Add more logging to identify unexpected error of Tempfile#close.
21
+ * out_s3: Support `checksum_algorithm` parameter to validate the data with checksum (CRC32, CRC32C, SHA1 and SHA256) during upload/download.
22
+
23
+ Release 1.8.1 - 2024/11/15
24
+
25
+ * dependency: Change zstd-ruby dependency optional. Install zstd-ruby manually if you want to enable Zstd compression feature.
26
+
27
+ Release 1.8.0 - 2024/11/06
28
+
29
+ * out_s3: Add zstd compression support
30
+
1
31
  Release 1.7.2 - 2022/10/19
2
32
 
3
33
  * in_s3: Add `event_bridge_mode` parameter
data/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # Amazon S3 plugin for [Fluentd](http://github.com/fluent/fluentd)
2
2
 
3
- [<img src="https://travis-ci.org/fluent/fluent-plugin-s3.svg?branch=master"
4
- alt="Build Status" />](https://travis-ci.org/fluent/fluent-plugin-s3) [<img
3
+ [![linux](https://github.com/fluent/fluent-plugin-s3/actions/workflows/linux.yml/badge.svg)](https://github.com/fluent/fluent-plugin-s3/actions/workflows/linux.yml) [<img
5
4
  src="https://codeclimate.com/github/fluent/fluent-plugin-s3/badges/gpa.svg"
6
5
  />](https://codeclimate.com/github/fluent/fluent-plugin-s3)
7
6
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.7.2
1
+ 1.8.5
data/docs/credentials.md CHANGED
@@ -14,6 +14,23 @@ AWS access key id.
14
14
 
15
15
  AWS secret key.
16
16
 
17
+ ### aws_profile
18
+
19
+ Specify a named profile from your shared AWS configuration files (`~/.aws/credentials` or `~/.aws/config`).
20
+ This is useful for environments using IAM Roles or short-lived credentials.
21
+
22
+ ### aws_credential_process
23
+
24
+ Specify a `credential_process` command directly in the Fluentd configuration.
25
+ This enables integration with external credential helpers like IAM Roles Anywhere.
26
+
27
+ <match *>
28
+ @type s3
29
+ # Example for IAM Roles Anywhere
30
+ aws_credential_process "/path/to/aws_signing_helper credential-process --certificate /path/to/cert.pem --private-key /path/to/key.pem --trust-anchor-arn <ARN> --profile-arn <ARN> --role-arn <ARN>"
31
+ # ... other settings
32
+ </match>
33
+
17
34
  ## \<assume_role_credentials\> section
18
35
 
19
36
  Typically, you use AssumeRole for cross-account access or federation.
@@ -126,7 +143,7 @@ See also:
126
143
 
127
144
  ### retries
128
145
 
129
- Number of times to retry when retrieving credentials. Default is 5.
146
+ Number of times to retry when retrieving credentials. Default is nil.
130
147
 
131
148
  ### ip_address
132
149
 
data/docs/input.md CHANGED
@@ -29,6 +29,11 @@ See also [Configuration: credentials](credentials.md) for common comprehensive p
29
29
 
30
30
  Whether or not object metadata should be added to the record. Defaults to `false`. See below for details.
31
31
 
32
+ ## decompression_size_limit
33
+
34
+ The size limit of the decompressed data. The default is `256m` (256 MiB).
35
+ This parameter is designed to prevent memory exhaustion when extracting highly compressed objects from S3.
36
+
32
37
  ## match_regexp
33
38
 
34
39
  If provided, process the S3 object only if its keys matches the regular expression
data/docs/output.md CHANGED
@@ -185,6 +185,7 @@ archive format on S3. You can use several format:
185
185
  * parquet (Need columnify command)
186
186
  * This compressor uses an external [columnify](https://github.com/reproio/columnify) command.
187
187
  * Use [`<compress>`](#compress-for-parquet-compressor-only) section to configure columnify command behavior.
188
+ * zstd (Need `zstd-ruby` gem, supported since v1.8.0)
188
189
 
189
190
  See [Use your compression algorithm](howto.md#use-your-compression-algorighm) section for adding another format.
190
191
 
@@ -208,7 +209,7 @@ parquet file page size. default: 8192 bytes
208
209
 
209
210
  ### parquet_row_group_size
210
211
 
211
- parquet file row group size. default: 128 MB
212
+ parquet file row group size. default: 128 MiB
212
213
 
213
214
  ### record_type
214
215
 
@@ -430,6 +431,18 @@ Specifies the AWS KMS key ID to use for object encryption.
430
431
 
431
432
  Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321.
432
433
 
434
+ ## checksum_algorithm
435
+
436
+ AWS allows to calculate the integrity checksum server side. The additional checksum is
437
+ used to validate the data during upload or download. The following 4 SHA and CRC algorithms are supported:
438
+
439
+ * CRC32
440
+ * CRC32C
441
+ * SHA1
442
+ * SHA256
443
+
444
+ For more info refer to [object integrity](https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html).
445
+
433
446
  ## compute_checksums
434
447
 
435
448
  AWS SDK uses MD5 for API request/response by default. On FIPS enabled environment,
@@ -23,7 +23,9 @@ Gem::Specification.new do |gem|
23
23
  gem.add_development_dependency "test-unit", ">= 3.0.8"
24
24
  gem.add_development_dependency "test-unit-rr", ">= 1.0.3"
25
25
  gem.add_development_dependency "timecop"
26
+ gem.add_development_dependency "ostruct"
26
27
  # aws-sdk-core requires one of ox, oga, libxml, nokogiri or rexml,
27
28
  # and rexml is no longer default gem as of Ruby 3.0.
28
29
  gem.add_development_dependency "rexml"
30
+ gem.add_development_dependency "zstd-ruby", ["~> 1.5"]
29
31
  end
@@ -8,6 +8,7 @@ require 'cgi/util'
8
8
  require 'zlib'
9
9
  require 'time'
10
10
  require 'tempfile'
11
+ require 'shellwords'
11
12
 
12
13
  module Fluent::Plugin
13
14
  class S3Input < Input
@@ -21,15 +22,22 @@ module Fluent::Plugin
21
22
  end
22
23
 
23
24
  DEFAULT_PARSE_TYPE = "none"
25
+ DECOMPRESSION_SIZE_LIMIT = 256 * 1024 * 1024
24
26
 
25
27
  desc "Use aws-sdk-ruby bundled cert"
26
28
  config_param :use_bundled_cert, :bool, default: false
27
29
  desc "Add object metadata to the records parsed out of a given object"
28
30
  config_param :add_object_metadata, :bool, default: false
31
+ desc 'The size limit of the extracted element.'
32
+ config_param :decompression_size_limit, :size, default: DECOMPRESSION_SIZE_LIMIT
29
33
  desc "AWS access key id"
30
34
  config_param :aws_key_id, :string, default: nil, secret: true
31
35
  desc "AWS secret key."
32
36
  config_param :aws_sec_key, :string, default: nil, secret: true
37
+ desc "AWS profile name."
38
+ config_param :aws_profile, :string, default: nil
39
+ desc "The command to run to generate credentials."
40
+ config_param :aws_credential_process, :string, default: nil
33
41
  config_section :assume_role_credentials, multi: false do
34
42
  desc "The Amazon Resource Name (ARN) of the role to assume"
35
43
  config_param :role_arn, :string
@@ -154,7 +162,7 @@ module Fluent::Plugin
154
162
 
155
163
  Aws.use_bundled_cert! if @use_bundled_cert
156
164
 
157
- @extractor = EXTRACTOR_REGISTRY.lookup(@store_as).new(log: log)
165
+ @extractor = EXTRACTOR_REGISTRY.lookup(@store_as).new(log: log, decompression_size_limit: @decompression_size_limit)
158
166
  @extractor.configure(conf)
159
167
 
160
168
  @parser = parser_create(conf: parser_config, default_type: DEFAULT_PARSE_TYPE)
@@ -205,13 +213,13 @@ module Fluent::Plugin
205
213
  begin
206
214
  @poller.poll(options) do |message|
207
215
  begin
208
- body = Yajl.load(message.body)
216
+ body = JSON.parse(message.body)
209
217
  log.debug(body)
210
218
  next unless is_valid_queue(body) # skip test queue
211
219
  if @match_regexp
212
220
  raw_key = get_raw_key(body)
213
221
  key = CGI.unescape(raw_key)
214
- next unless @match_regexp.match?(key)
222
+ next unless @match_regexp.match?(key)
215
223
  end
216
224
  process(body)
217
225
  rescue => e
@@ -231,7 +239,7 @@ module Fluent::Plugin
231
239
  if @sqs.event_bridge_mode
232
240
  log.debug("checking for eventbridge property")
233
241
  !!body["detail"]
234
- else
242
+ else
235
243
  log.debug("checking for Records property")
236
244
  !!body["Records"]
237
245
  end
@@ -242,7 +250,7 @@ module Fluent::Plugin
242
250
  body["detail"]["object"]["key"]
243
251
  else
244
252
  body["Records"].first["s3"]["object"]["key"]
245
- end
253
+ end
246
254
  end
247
255
 
248
256
  def setup_credentials
@@ -252,6 +260,10 @@ module Fluent::Plugin
252
260
  when @aws_key_id && @aws_sec_key
253
261
  options[:access_key_id] = @aws_key_id
254
262
  options[:secret_access_key] = @aws_sec_key
263
+ when @aws_profile
264
+ options[:profile] = @aws_profile
265
+ when @aws_credential_process
266
+ options[:credentials] = Aws::ProcessCredentials.new(Shellwords.split(@aws_credential_process))
255
267
  when @assume_role_credentials
256
268
  c = @assume_role_credentials
257
269
  credentials_options[:role_arn] = c.role_arn
@@ -356,13 +368,18 @@ module Fluent::Plugin
356
368
  end
357
369
 
358
370
  class Extractor
371
+ class SizeLimitError < StandardError; end
372
+
359
373
  include Fluent::Configurable
360
374
 
361
375
  attr_reader :log
362
376
 
363
- def initialize(log: $log, **options)
377
+ BYTES_TO_READ = 64 * 1024
378
+
379
+ def initialize(log: $log, decompression_size_limit: DECOMPRESSION_SIZE_LIMIT, **options)
364
380
  super()
365
381
  @log = log
382
+ @decompression_size_limit = decompression_size_limit
366
383
  end
367
384
 
368
385
  def configure(conf)
@@ -390,6 +407,38 @@ module Fluent::Plugin
390
407
  raise Fluent::ConfigError, "'#{command}' utility must be in PATH for #{algo} compression"
391
408
  end
392
409
  end
410
+
411
+ def extract_with_command(command, io, tempfile_basename = "s3-extractor-tmp")
412
+ path = if io.respond_to?(:path)
413
+ io.path
414
+ else
415
+ extractor = TextExtractor.new(log: log, decompression_size_limit: @decompression_size_limit)
416
+ temp = Tempfile.new(tempfile_basename)
417
+ temp.write(extractor.extract(io))
418
+ temp.close
419
+ temp.path
420
+ end
421
+
422
+ out = ''
423
+ begin
424
+ Open3.popen3("#{command} #{path}") do |stdin, stdout, stderr, wait_thr|
425
+ stdin.close
426
+ while (chunk = stdout.read(BYTES_TO_READ))
427
+ out << chunk
428
+ if out.bytesize > @decompression_size_limit
429
+ Process.kill("TERM", wait_thr.pid) rescue nil
430
+ raise SizeLimitError, "Extracted data exceeds limit of #{@decompression_size_limit} bytes"
431
+ end
432
+ end
433
+
434
+ if wait_thr.value.success?
435
+ out
436
+ else
437
+ raise "Command execution failed: #{command} (status: #{wait_thr.value})"
438
+ end
439
+ end
440
+ end
441
+ end
393
442
  end
394
443
 
395
444
  class GzipExtractor < Extractor
@@ -405,19 +454,25 @@ module Fluent::Plugin
405
454
  # https://bugs.ruby-lang.org/issues/11180
406
455
  # https://github.com/exAspArk/multiple_files_gzip_reader
407
456
  def extract(io)
408
- parts = []
457
+ out = ''
409
458
  loop do
410
459
  unused = nil
411
460
  Zlib::GzipReader.wrap(io) do |gz|
412
- parts << gz.read
461
+ while (chunk = gz.read(BYTES_TO_READ))
462
+ out << chunk
463
+ if out.bytesize > @decompression_size_limit
464
+ raise SizeLimitError, "Extracted data exceeds limit of #{@decompression_size_limit} bytes"
465
+ end
466
+ end
413
467
  unused = gz.unused
414
468
  gz.finish
415
469
  end
416
470
  io.pos -= unused ? unused.length : 0
417
471
  break if io.eof?
418
472
  end
419
- io.close
420
- parts.join
473
+ out
474
+ ensure
475
+ io.close unless io.closed?
421
476
  end
422
477
  end
423
478
 
@@ -431,7 +486,14 @@ module Fluent::Plugin
431
486
  end
432
487
 
433
488
  def extract(io)
434
- io.read
489
+ out = ''
490
+ while (chunk = io.read(BYTES_TO_READ))
491
+ out << chunk
492
+ if out.bytesize > @decompression_size_limit
493
+ raise SizeLimitError, "Extracted data exceeds limit of #{@decompression_size_limit} bytes"
494
+ end
495
+ end
496
+ out
435
497
  end
436
498
  end
437
499
 
@@ -6,6 +6,7 @@ require 'zlib'
6
6
  require 'time'
7
7
  require 'tempfile'
8
8
  require 'securerandom'
9
+ require 'shellwords'
9
10
 
10
11
  module Fluent::Plugin
11
12
  class S3Output < Output
@@ -29,6 +30,10 @@ module Fluent::Plugin
29
30
  config_param :aws_key_id, :string, default: nil, secret: true
30
31
  desc "AWS secret key."
31
32
  config_param :aws_sec_key, :string, default: nil, secret: true
33
+ desc "AWS profile name."
34
+ config_param :aws_profile, :string, default: nil
35
+ desc "The command to run to generate credentials."
36
+ config_param :aws_credential_process, :string, default: nil
32
37
  config_section :assume_role_credentials, multi: false do
33
38
  desc "The Amazon Resource Name (ARN) of the role to assume"
34
39
  config_param :role_arn, :string, secret: true
@@ -62,6 +67,10 @@ module Fluent::Plugin
62
67
  config_param :duration_seconds, :integer, default: nil
63
68
  desc "The region of the STS endpoint to use."
64
69
  config_param :sts_region, :string, default: nil
70
+ desc "A http proxy url for requests to aws sts service"
71
+ config_param :sts_http_proxy, :string, default: nil, secret: true
72
+ desc "A url for a regional sts api endpoint, the default is global"
73
+ config_param :sts_endpoint_url, :string, default: nil
65
74
  end
66
75
  config_section :instance_profile_credentials, multi: false do
67
76
  desc "Number of times to retry when retrieving credentials"
@@ -154,6 +163,8 @@ module Fluent::Plugin
154
163
  desc "Arbitrary S3 tag-set for the object"
155
164
  config_param :tagging, :string, default: nil
156
165
  desc "Arbitrary S3 metadata headers to set for the object"
166
+ config_param :checksum_algorithm, :string, default: nil
167
+ desc "Indicates the algorithm you want Amazon S3 to use to create the checksum for the object (CRC32,CRC32C,SHA1,SHA256)"
157
168
  config_param :s3_metadata, :hash, default: nil
158
169
  config_section :bucket_lifecycle_rule, param_name: :bucket_lifecycle_rules, multi: true do
159
170
  desc "A unique ID for this rule"
@@ -197,7 +208,7 @@ module Fluent::Plugin
197
208
 
198
209
  begin
199
210
  buffer_type = @buffer_config[:@type]
200
- @compressor = COMPRESSOR_REGISTRY.lookup(@store_as).new(buffer_type: buffer_type, log: log)
211
+ @compressor = COMPRESSOR_REGISTRY.lookup(@store_as).new(buffer_type: buffer_type, buffer_compressed_type: @buffer.compress, log: log)
201
212
  rescue => e
202
213
  log.warn "'#{@store_as}' not supported. Use 'text' instead: error = #{e.message}"
203
214
  @compressor = TextCompressor.new
@@ -368,6 +379,7 @@ module Fluent::Plugin
368
379
  put_options[:grant_read] = @grant_read if @grant_read
369
380
  put_options[:grant_read_acp] = @grant_read_acp if @grant_read_acp
370
381
  put_options[:grant_write_acp] = @grant_write_acp if @grant_write_acp
382
+ put_options[:checksum_algorithm] = @checksum_algorithm if @checksum_algorithm
371
383
  put_options[:tagging] = @tagging if @tagging
372
384
 
373
385
  if @s3_metadata
@@ -378,6 +390,8 @@ module Fluent::Plugin
378
390
  end
379
391
  @bucket.object(s3path).put(put_options)
380
392
 
393
+ log.debug "out_s3: completed to write chunk #{dump_unique_id_hex(chunk.unique_id)} with metadata #{chunk.metadata} to s3://#{@s3_bucket}/#{s3path}"
394
+
381
395
  @values_for_s3_object_chunk.delete(chunk.unique_id)
382
396
 
383
397
  if @warn_for_delay
@@ -386,7 +400,11 @@ module Fluent::Plugin
386
400
  end
387
401
  end
388
402
  ensure
389
- tmp.close(true) rescue nil
403
+ begin
404
+ tmp.close(true)
405
+ rescue => e
406
+ log.info "out_s3: Tempfile#close caused unexpected error", error: e
407
+ end
390
408
  end
391
409
  end
392
410
 
@@ -529,17 +547,28 @@ module Fluent::Plugin
529
547
  when @aws_key_id && @aws_sec_key
530
548
  options[:access_key_id] = @aws_key_id
531
549
  options[:secret_access_key] = @aws_sec_key
550
+ when @aws_profile
551
+ options[:profile] = @aws_profile
552
+ when @aws_credential_process
553
+ options[:credentials] = Aws::ProcessCredentials.new(Shellwords.split(@aws_credential_process))
532
554
  when @web_identity_credentials
533
555
  c = @web_identity_credentials
556
+ region = c.sts_region || @s3_region
534
557
  credentials_options[:role_arn] = c.role_arn
535
558
  credentials_options[:role_session_name] = c.role_session_name
536
559
  credentials_options[:web_identity_token_file] = c.web_identity_token_file
537
560
  credentials_options[:policy] = c.policy if c.policy
538
561
  credentials_options[:duration_seconds] = c.duration_seconds if c.duration_seconds
539
- if c.sts_region
540
- credentials_options[:client] = Aws::STS::Client.new(:region => c.sts_region)
541
- elsif @s3_region
542
- credentials_options[:client] = Aws::STS::Client.new(:region => @s3_region)
562
+ credentials_options[:sts_endpoint_url] = c.sts_endpoint_url if c.sts_endpoint_url
563
+ credentials_options[:sts_http_proxy] = c.sts_http_proxy if c.sts_http_proxy
564
+ if c.sts_http_proxy && c.sts_endpoint_url
565
+ credentials_options[:client] = Aws::STS::Client.new(region: region, http_proxy: c.sts_http_proxy, endpoint: c.sts_endpoint_url)
566
+ elsif c.sts_http_proxy
567
+ credentials_options[:client] = Aws::STS::Client.new(region: region, http_proxy: c.sts_http_proxy)
568
+ elsif c.sts_endpoint_url
569
+ credentials_options[:client] = Aws::STS::Client.new(region: region, endpoint: c.sts_endpoint_url)
570
+ else
571
+ credentials_options[:client] = Aws::STS::Client.new(region: region)
543
572
  end
544
573
  options[:credentials] = Aws::AssumeRoleWebIdentityCredentials.new(credentials_options)
545
574
  when @instance_profile_credentials
@@ -580,6 +609,7 @@ module Fluent::Plugin
580
609
  def initialize(opts = {})
581
610
  super()
582
611
  @buffer_type = opts[:buffer_type]
612
+ @buffer_compressed_type = opts[:buffer_compressed_type]
583
613
  @log = opts[:log]
584
614
  end
585
615
 
@@ -622,11 +652,18 @@ module Fluent::Plugin
622
652
  end
623
653
 
624
654
  def compress(chunk, tmp)
655
+ if @buffer_compressed_type == :gzip
656
+ chunk.write_to(tmp, compressed: @buffer_compressed_type)
657
+ return
658
+ end
659
+
625
660
  w = Zlib::GzipWriter.new(tmp)
626
661
  chunk.write_to(w)
627
662
  w.finish
628
663
  ensure
629
- w.finish rescue nil
664
+ if w
665
+ w.finish rescue nil
666
+ end
630
667
  end
631
668
  end
632
669
 
@@ -19,6 +19,11 @@ module Fluent::Plugin
19
19
  end
20
20
 
21
21
  def compress(chunk, tmp)
22
+ if @buffer_compressed_type == :gzip
23
+ chunk.write_to(tmp, compressed: @buffer_compressed_type)
24
+ return
25
+ end
26
+
22
27
  chunk_is_file = @buffer_type == 'file'
23
28
  path = if chunk_is_file
24
29
  chunk.path
@@ -0,0 +1,41 @@
1
+ module Fluent::Plugin
2
+ class S3Output
3
+ class ZstdCompressor < Compressor
4
+ S3Output.register_compressor('zstd', self)
5
+
6
+ config_section :compress, param_name: :compress_config, init: true, multi: false do
7
+ desc "Compression level for zstd (1-22)"
8
+ config_param :level, :integer, default: 3
9
+ end
10
+
11
+ def initialize(opts = {})
12
+ super
13
+ require 'zstd-ruby'
14
+ rescue LoadError => e
15
+ log.error "failed to load zstd-ruby gem. You need to manually install 'zstd-ruby' gem to use 'zstd'.", error: e.message
16
+ raise Fluent::ConfigError, "failed to load 'zstd-ruby' gem"
17
+ end
18
+
19
+ def ext
20
+ 'zst'.freeze
21
+ end
22
+
23
+ def content_type
24
+ 'application/x-zst'.freeze
25
+ end
26
+
27
+ def compress(chunk, tmp)
28
+ if @buffer_compressed_type == :zstd
29
+ chunk.write_to(tmp, compressed: @buffer_compressed_type)
30
+ return
31
+ end
32
+
33
+ compressed = Zstd.compress(chunk.read, level: @compress_config.level)
34
+ tmp.write(compressed)
35
+ rescue => e
36
+ log.warn "zstd compression failed: #{e.message}"
37
+ raise
38
+ end
39
+ end
40
+ end
41
+ end