fluent-plugin-s3 1.8.3 → 1.8.4

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: a674e172940ab48c2892af28f310ed186d382f34c73da8d9ac01287965691110
4
- data.tar.gz: ed90652d734c43c099b58d050214de9d3a033ea38b94dd9b1c9dc0a427dc9de4
3
+ metadata.gz: e3de70d5f20b42bd86ce425d436b2af2fabcb12bcd11756d6ccb311d32f117e7
4
+ data.tar.gz: 8896f6755b9c7cb6c726950493164bf4275cf0776b16cd37dd660ffe511e2f16
5
5
  SHA512:
6
- metadata.gz: 677cc94165eeb960faf1af30fa2f1a31df4cbafa694801758f45eca7101a5370192299207a47c9b7c3fce973a4c87e97aaa53e420fdfc6b76a5ab2d1f9315c88
7
- data.tar.gz: 7b974daffad50c509fe40c5574ba63240ec75d86480d74a1ab31a9ff85018356dc3be7573965b8c9245523c2bbc4fd699da8f54a6aeeccda2a8316d57d04d9c6
6
+ metadata.gz: d9bd5499f054de826654d44208858684917d226f229fb4eb3e9f04cfe09dad089345fa16333ed05848499e4dd4b5ab321b36e7dc6dec733f0f2110f280b07512
7
+ data.tar.gz: f21db8ac19f9a3e05502f39f0e32d3740a903431800df70871539404fb3fdb4226b0c974c0f65d2e5f7e32f177a78df90a8529314fc84a9c1de4d051bac577e0
@@ -10,12 +10,12 @@ jobs:
10
10
  strategy:
11
11
  fail-fast: false
12
12
  matrix:
13
- ruby: [ '3.4', '3.3', '3.2', '3.1', '3.0', '2.7' ]
13
+ ruby: [ '4.0', '3.4', '3.3', '3.2', '3.1', '3.0', '2.7' ]
14
14
  os:
15
15
  - ubuntu-latest
16
16
  name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }}
17
17
  steps:
18
- - uses: actions/checkout@v4
18
+ - uses: actions/checkout@v6
19
19
  - uses: ruby/setup-ruby@v1
20
20
  with:
21
21
  ruby-version: ${{ matrix.ruby }}
@@ -7,7 +7,7 @@ jobs:
7
7
  stale:
8
8
  runs-on: ubuntu-latest
9
9
  steps:
10
- - uses: actions/stale@v8
10
+ - uses: actions/stale@v10
11
11
  with:
12
12
  repo-token: ${{ secrets.GITHUB_TOKEN }}
13
13
  days-before-stale: 30
data/ChangeLog CHANGED
@@ -1,3 +1,12 @@
1
+ Release 1.8.4 - 2026/03/04
2
+
3
+ * in_s3: add aws_profile / aws_credential_process parameters for credencials (GitHub: #464)
4
+ * out_s3: add aws_profile / aws_credential_process parameters for credencials (GitHub: #464)
5
+ * out_s3: stop unnecessary re-compression when chunk is already compressed (GitHub: #460)
6
+ * gem: use same zstd-ruby version with Fluentd (GitHub: #463)
7
+ * gem: add ostruct gem as dependency for Ruby 4.0 (GitHub: #461)
8
+ * docs: add missing zstd compression (GitHub: #459)
9
+
1
10
  Release 1.8.3 - 2025/02/18
2
11
 
3
12
  * out_s3: Add `sts_http_proxy` and `sts_endpoint_url` to web_identity_credentials (GitHub: #452)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.8.3
1
+ 1.8.4
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.
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
 
@@ -23,8 +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"
29
- gem.add_development_dependency "zstd-ruby"
30
+ gem.add_development_dependency "zstd-ruby", ["~> 1.5"]
30
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
@@ -30,6 +31,10 @@ module Fluent::Plugin
30
31
  config_param :aws_key_id, :string, default: nil, secret: true
31
32
  desc "AWS secret key."
32
33
  config_param :aws_sec_key, :string, default: nil, secret: true
34
+ desc "AWS profile name."
35
+ config_param :aws_profile, :string, default: nil
36
+ desc "The command to run to generate credentials."
37
+ config_param :aws_credential_process, :string, default: nil
33
38
  config_section :assume_role_credentials, multi: false do
34
39
  desc "The Amazon Resource Name (ARN) of the role to assume"
35
40
  config_param :role_arn, :string
@@ -211,7 +216,7 @@ module Fluent::Plugin
211
216
  if @match_regexp
212
217
  raw_key = get_raw_key(body)
213
218
  key = CGI.unescape(raw_key)
214
- next unless @match_regexp.match?(key)
219
+ next unless @match_regexp.match?(key)
215
220
  end
216
221
  process(body)
217
222
  rescue => e
@@ -231,7 +236,7 @@ module Fluent::Plugin
231
236
  if @sqs.event_bridge_mode
232
237
  log.debug("checking for eventbridge property")
233
238
  !!body["detail"]
234
- else
239
+ else
235
240
  log.debug("checking for Records property")
236
241
  !!body["Records"]
237
242
  end
@@ -242,7 +247,7 @@ module Fluent::Plugin
242
247
  body["detail"]["object"]["key"]
243
248
  else
244
249
  body["Records"].first["s3"]["object"]["key"]
245
- end
250
+ end
246
251
  end
247
252
 
248
253
  def setup_credentials
@@ -252,6 +257,10 @@ module Fluent::Plugin
252
257
  when @aws_key_id && @aws_sec_key
253
258
  options[:access_key_id] = @aws_key_id
254
259
  options[:secret_access_key] = @aws_sec_key
260
+ when @aws_profile
261
+ options[:profile] = @aws_profile
262
+ when @aws_credential_process
263
+ options[:credentials] = Aws::ProcessCredentials.new(Shellwords.split(@aws_credential_process))
255
264
  when @assume_role_credentials
256
265
  c = @assume_role_credentials
257
266
  credentials_options[:role_arn] = c.role_arn
@@ -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
@@ -203,7 +208,7 @@ module Fluent::Plugin
203
208
 
204
209
  begin
205
210
  buffer_type = @buffer_config[:@type]
206
- @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)
207
212
  rescue => e
208
213
  log.warn "'#{@store_as}' not supported. Use 'text' instead: error = #{e.message}"
209
214
  @compressor = TextCompressor.new
@@ -542,6 +547,10 @@ module Fluent::Plugin
542
547
  when @aws_key_id && @aws_sec_key
543
548
  options[:access_key_id] = @aws_key_id
544
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))
545
554
  when @web_identity_credentials
546
555
  c = @web_identity_credentials
547
556
  region = c.sts_region || @s3_region
@@ -600,6 +609,7 @@ module Fluent::Plugin
600
609
  def initialize(opts = {})
601
610
  super()
602
611
  @buffer_type = opts[:buffer_type]
612
+ @buffer_compressed_type = opts[:buffer_compressed_type]
603
613
  @log = opts[:log]
604
614
  end
605
615
 
@@ -642,11 +652,18 @@ module Fluent::Plugin
642
652
  end
643
653
 
644
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
+
645
660
  w = Zlib::GzipWriter.new(tmp)
646
661
  chunk.write_to(w)
647
662
  w.finish
648
663
  ensure
649
- w.finish rescue nil
664
+ if w
665
+ w.finish rescue nil
666
+ end
650
667
  end
651
668
  end
652
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
@@ -25,6 +25,11 @@ module Fluent::Plugin
25
25
  end
26
26
 
27
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
+
28
33
  compressed = Zstd.compress(chunk.read, level: @compress_config.level)
29
34
  tmp.write(compressed)
30
35
  rescue => e
data/test/test_in_s3.rb CHANGED
@@ -71,6 +71,32 @@ class S3InputTest < Test::Unit::TestCase
71
71
  end
72
72
  end
73
73
 
74
+ def test_aws_profile
75
+ d = create_driver(%[
76
+ aws_profile test_profile
77
+ s3_bucket test_bucket
78
+ buffer_type memory
79
+ <sqs>
80
+ queue_name test_queue
81
+ queue_owner_aws_account_id 123456789123
82
+ </sqs>
83
+ ])
84
+ assert_equal 'test_profile', d.instance.aws_profile
85
+ end
86
+
87
+ def test_aws_credential_process
88
+ d = create_driver(%[
89
+ aws_credential_process "/path/to/aws_signing_helper credential-process"
90
+ s3_bucket test_bucket
91
+ buffer_type memory
92
+ <sqs>
93
+ queue_name test_queue
94
+ queue_owner_aws_account_id 123456789123
95
+ </sqs>
96
+ ])
97
+ assert_equal '/path/to/aws_signing_helper credential-process', d.instance.aws_credential_process
98
+ end
99
+
74
100
  def test_without_sqs_section
75
101
  conf = %[
76
102
  aws_key_id test_key_id
@@ -708,7 +734,7 @@ EOS
708
734
  }
709
735
  }
710
736
  }
711
-
737
+
712
738
  message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
713
739
  @sqs_poller.get_messages(anything, anything) do |config, stats|
714
740
  config.before_request.call(stats) if config.before_request
data/test/test_out_s3.rb CHANGED
@@ -2,6 +2,7 @@ require 'fluent/test'
2
2
  require 'fluent/test/helpers'
3
3
  require 'fluent/test/log'
4
4
  require 'fluent/test/driver/output'
5
+ require 'fluent/version'
5
6
  require 'aws-sdk-s3'
6
7
  require 'fluent/plugin/out_s3'
7
8
 
@@ -68,6 +69,30 @@ class S3OutputTest < Test::Unit::TestCase
68
69
  assert_equal true, d.instance.check_object
69
70
  end
70
71
 
72
+ def test_aws_profile
73
+ d = create_driver(%[
74
+ aws_profile test_profile
75
+ s3_bucket test_bucket
76
+ path log
77
+ utc
78
+ buffer_type memory
79
+ time_slice_format %Y%m%d-%H
80
+ ])
81
+ assert_equal 'test_profile', d.instance.aws_profile
82
+ end
83
+
84
+ def test_aws_credential_process
85
+ d = create_driver(%[
86
+ aws_credential_process "/path/to/aws_signing_helper credential-process"
87
+ s3_bucket test_bucket
88
+ path log
89
+ utc
90
+ buffer_type memory
91
+ time_slice_format %Y%m%d-%H
92
+ ])
93
+ assert_equal '/path/to/aws_signing_helper credential-process', d.instance.aws_credential_process
94
+ end
95
+
71
96
  def test_s3_endpoint_with_valid_endpoint
72
97
  d = create_driver(CONFIG + 's3_endpoint riak-cs.example.com')
73
98
  assert_equal 'riak-cs.example.com', d.instance.s3_endpoint
@@ -495,6 +520,125 @@ EOC
495
520
  FileUtils.rm_f(s3_local_file_path)
496
521
  end
497
522
 
523
+ def test_no_compress_by_gzip_with_compressed_buffer
524
+ setup_mocks(true)
525
+ s3_local_file_path = "/tmp/s3-test.gz"
526
+ setup_s3_object_mocks(s3_local_file_path: s3_local_file_path)
527
+
528
+ d = create_time_sliced_driver(CONFIG_TIME_SLICE + <<~EOF)
529
+ <buffer tag,time>
530
+ @type memory
531
+ compress gzip
532
+ timekey 3600
533
+ timekey_use_utc true
534
+ </buffer>
535
+ EOF
536
+
537
+ # GzipCompressor should not use Zlib::GzipWriter
538
+ dont_allow(Zlib::GzipWriter).new
539
+
540
+ time = event_time("2011-01-02 13:14:15 UTC")
541
+ d.run(default_tag: "test") do
542
+ d.feed(time, {"a"=>1})
543
+ d.feed(time, {"a"=>2})
544
+ end
545
+
546
+ data = ""
547
+ File.open(s3_local_file_path, "rb") do |f|
548
+ until f.eof?
549
+ gz = Zlib::GzipReader.new(f)
550
+ data << gz.read
551
+
552
+ unused = gz.unused
553
+ gz.finish
554
+ f.pos -= unused.length if unused
555
+ end
556
+ end
557
+ assert_equal %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] +
558
+ %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n],
559
+ data
560
+ FileUtils.rm_f(s3_local_file_path)
561
+ end
562
+
563
+ def test_no_compress_by_gzip_command_with_compressed_buffer
564
+ setup_mocks(true)
565
+ s3_local_file_path = "/tmp/s3-test.gz"
566
+ setup_s3_object_mocks(s3_local_file_path: s3_local_file_path)
567
+
568
+ d = create_time_sliced_driver(CONFIG_TIME_SLICE + <<~EOF)
569
+ store_as gzip_command
570
+ <buffer tag,time>
571
+ @type memory
572
+ compress gzip
573
+ timekey 3600
574
+ timekey_use_utc true
575
+ </buffer>
576
+ EOF
577
+
578
+ # GzipCommandCompressor should not use Kernel.system and Zlib::GzipWriter
579
+ dont_allow(Kernel).system
580
+ dont_allow(Zlib::GzipWriter).new
581
+
582
+ time = event_time("2011-01-02 13:14:15 UTC")
583
+ d.run(default_tag: "test") do
584
+ d.feed(time, {"a"=>1})
585
+ d.feed(time, {"a"=>2})
586
+ end
587
+
588
+ data = ""
589
+ File.open(s3_local_file_path, "rb") do |f|
590
+ until f.eof?
591
+ gz = Zlib::GzipReader.new(f)
592
+ data << gz.read
593
+
594
+ unused = gz.unused
595
+ gz.finish
596
+ f.pos -= unused.length if unused
597
+ end
598
+ end
599
+ assert_equal %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] +
600
+ %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n],
601
+ data
602
+ FileUtils.rm_f(s3_local_file_path)
603
+ end
604
+
605
+ def test_no_compress_by_zstd_with_compressed_buffer
606
+ pend "This test require Fluentd 1.19.0 or later which supports zstd buffer compressed." if Gem::Version.new(Fluent::VERSION) < Gem::Version.new('1.19.0')
607
+
608
+ setup_mocks(true)
609
+ s3_local_file_path = "/tmp/s3-test.zst"
610
+ expected_s3path = "log/events/ts=20110102-13/events_0-#{Socket.gethostname}.zst"
611
+ setup_s3_object_mocks(s3_local_file_path: s3_local_file_path, s3path: expected_s3path)
612
+
613
+ d = create_time_sliced_driver(CONFIG_TIME_SLICE + <<~EOF)
614
+ store_as zstd
615
+ <buffer tag,time>
616
+ @type memory
617
+ compress zstd
618
+ timekey 3600
619
+ timekey_use_utc true
620
+ </buffer>
621
+ EOF
622
+
623
+ # ZstdCompressor should not use Zstd.compress
624
+ dont_allow(Zstd).compress
625
+
626
+ time = event_time("2011-01-02 13:14:15 UTC")
627
+ d.run(default_tag: "test") do
628
+ d.feed(time, {"a"=>1})
629
+ d.feed(time, {"a"=>2})
630
+ end
631
+
632
+ File.open(s3_local_file_path, 'rb') do |file|
633
+ compressed_data = file.read
634
+ uncompressed_data = Zstd.decompress(compressed_data)
635
+ expected_data = %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] +
636
+ %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n]
637
+ assert_equal expected_data, uncompressed_data
638
+ end
639
+ FileUtils.rm_f(s3_local_file_path)
640
+ end
641
+
498
642
  class MockResponse
499
643
  attr_reader :data
500
644
 
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-s3
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.3
4
+ version: 1.8.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sadayuki Furuhashi
8
8
  - Masahiro Nakagawa
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2025-02-18 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: fluentd
@@ -116,7 +115,7 @@ dependencies:
116
115
  - !ruby/object:Gem::Version
117
116
  version: '0'
118
117
  - !ruby/object:Gem::Dependency
119
- name: rexml
118
+ name: ostruct
120
119
  requirement: !ruby/object:Gem::Requirement
121
120
  requirements:
122
121
  - - ">="
@@ -130,7 +129,7 @@ dependencies:
130
129
  - !ruby/object:Gem::Version
131
130
  version: '0'
132
131
  - !ruby/object:Gem::Dependency
133
- name: zstd-ruby
132
+ name: rexml
134
133
  requirement: !ruby/object:Gem::Requirement
135
134
  requirements:
136
135
  - - ">="
@@ -143,6 +142,20 @@ dependencies:
143
142
  - - ">="
144
143
  - !ruby/object:Gem::Version
145
144
  version: '0'
145
+ - !ruby/object:Gem::Dependency
146
+ name: zstd-ruby
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '1.5'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '1.5'
146
159
  description: Amazon S3 output plugin for Fluentd event collector
147
160
  email: frsyuki@gmail.com
148
161
  executables: []
@@ -187,7 +200,6 @@ homepage: https://github.com/fluent/fluent-plugin-s3
187
200
  licenses:
188
201
  - Apache-2.0
189
202
  metadata: {}
190
- post_install_message:
191
203
  rdoc_options: []
192
204
  require_paths:
193
205
  - lib
@@ -202,8 +214,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
202
214
  - !ruby/object:Gem::Version
203
215
  version: '0'
204
216
  requirements: []
205
- rubygems_version: 3.5.22
206
- signing_key:
217
+ rubygems_version: 4.0.6
207
218
  specification_version: 4
208
219
  summary: Amazon S3 output plugin for Fluentd event collector
209
- test_files: []
220
+ test_files:
221
+ - test/test_in_s3.rb
222
+ - test/test_out_s3.rb