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.
@@ -19,26 +19,15 @@ module Fluent::Plugin
19
19
  end
20
20
 
21
21
  def extract(io)
22
- path = if io.respond_to?(:path)
23
- io.path
24
- else
25
- temp = Tempfile.new("gzip-temp")
26
- temp.write(io.read)
27
- temp.close
28
- temp.path
29
- end
30
-
31
- stdout, succeeded = Open3.capture2("gzip #{@command_parameter} #{path}")
32
- if succeeded
33
- stdout
34
- else
35
- log.warn "failed to execute gzip command. Fallback to GzipReader. status = #{succeeded}"
36
- begin
37
- io.rewind
38
- Zlib::GzipReader.wrap(io) do |gz|
39
- gz.read
40
- end
41
- end
22
+ begin
23
+ extract_with_command("gzip #{@command_parameter}", io, "gzip-temp")
24
+ rescue SizeLimitError
25
+ raise
26
+ rescue => e
27
+ log.warn "gzip command execution failed: #{e.message}. Fallback to GzipExtractor."
28
+ io.rewind
29
+ extractor = GzipExtractor.new(log: log, decompression_size_limit: @decompression_size_limit)
30
+ extractor.extract(io)
42
31
  end
43
32
  end
44
33
  end
@@ -19,19 +19,11 @@ module Fluent::Plugin
19
19
  end
20
20
 
21
21
  def extract(io)
22
- path = if io.respond_to?(path)
23
- io.path
24
- else
25
- temp = Tempfile.new("xz-temp")
26
- temp.write(io.read)
27
- temp.close
28
- temp.path
29
- end
30
-
31
- stdout, succeeded = Open3.capture2("xz #{@command_parameter} #{path}")
32
- if succeeded
33
- stdout
34
- else
22
+ begin
23
+ extract_with_command("xz #{@command_parameter}", io, "xz-temp")
24
+ rescue SizeLimitError
25
+ raise
26
+ rescue
35
27
  raise "Failed to extract #{path} with xz command."
36
28
  end
37
29
  end
@@ -19,19 +19,11 @@ module Fluent::Plugin
19
19
  end
20
20
 
21
21
  def extract(io)
22
- path = if io.respond_to?(path)
23
- io.path
24
- else
25
- temp = Tempfile.new("lzop-temp")
26
- temp.write(io.read)
27
- temp.close
28
- temp.path
29
- end
30
-
31
- stdout, succeeded = Open3.capture2("lzop #{@command_parameter} #{path}")
32
- if succeeded
33
- stdout
34
- else
22
+ begin
23
+ extract_with_command("lzop #{@command_parameter}", io, "lzop-temp")
24
+ rescue SizeLimitError
25
+ raise
26
+ rescue
35
27
  raise "Failed to extract #{path} with lzop command."
36
28
  end
37
29
  end
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
@@ -315,7 +341,7 @@ EOS
315
341
  }
316
342
  ]
317
343
  }
318
- message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
344
+ message = Struct::StubMessage.new(1, 1, JSON.generate(body))
319
345
  @sqs_poller.get_messages(anything, anything) do |config, stats|
320
346
  config.before_request.call(stats) if config.before_request
321
347
  stats.request_count += 1
@@ -350,7 +376,7 @@ EOS
350
376
  }
351
377
  ]
352
378
  }
353
- message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
379
+ message = Struct::StubMessage.new(1, 1, JSON.generate(body))
354
380
  @sqs_poller.get_messages(anything, anything) do |config, stats|
355
381
  config.before_request.call(stats) if config.before_request
356
382
  stats.request_count += 1
@@ -385,7 +411,7 @@ EOS
385
411
  }
386
412
  ]
387
413
  }
388
- message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
414
+ message = Struct::StubMessage.new(1, 1, JSON.generate(body))
389
415
  @sqs_poller.get_messages(anything, anything) do |config, stats|
390
416
  config.before_request.call(stats) if config.before_request
391
417
  stats.request_count += 1
@@ -420,7 +446,7 @@ EOS
420
446
  }
421
447
  ]
422
448
  }
423
- message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
449
+ message = Struct::StubMessage.new(1, 1, JSON.generate(body))
424
450
  @sqs_poller.get_messages(anything, anything) do |config, stats|
425
451
  config.before_request.call(stats) if config.before_request
426
452
  stats.request_count += 1
@@ -455,7 +481,7 @@ EOS
455
481
  }
456
482
  ]
457
483
  }
458
- message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
484
+ message = Struct::StubMessage.new(1, 1, JSON.generate(body))
459
485
  @sqs_poller.get_messages(anything, anything) do |config, stats|
460
486
  config.before_request.call(stats) if config.before_request
461
487
  stats.request_count += 1
@@ -495,7 +521,7 @@ EOS
495
521
  }
496
522
  ]
497
523
  }
498
- message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
524
+ message = Struct::StubMessage.new(1, 1, JSON.generate(body))
499
525
  @sqs_poller.get_messages(anything, anything) do |config, stats|
500
526
  config.before_request.call(stats) if config.before_request
501
527
  stats.request_count += 1
@@ -543,7 +569,7 @@ EOS
543
569
  }
544
570
  ]
545
571
  }
546
- message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
572
+ message = Struct::StubMessage.new(1, 1, JSON.generate(body))
547
573
  @sqs_poller.get_messages(anything, anything) do |config, stats|
548
574
  config.before_request.call(stats) if config.before_request
549
575
  stats.request_count += 1
@@ -594,7 +620,7 @@ EOS
594
620
  }
595
621
  ]
596
622
  }
597
- message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
623
+ message = Struct::StubMessage.new(1, 1, JSON.generate(body))
598
624
  @sqs_poller.get_messages(anything, anything) do |config, stats|
599
625
  config.before_request.call(stats) if config.before_request
600
626
  stats.request_count += 1
@@ -614,6 +640,91 @@ EOS
614
640
  assert_equal(expected_records, events.map {|_tag, _time, record| record })
615
641
  end
616
642
 
643
+ data(
644
+ "limit_gzip" => { type: "gzip", input: "StringIO", limit: 10, expected_error: true },
645
+ "limit_text" => { type: "text", input: "StringIO", limit: 10, expected_error: true },
646
+ "limit_gzip_command1" => { type: "gzip_command", input: "StringIO", limit: 10, expected_error: true },
647
+ "limit_gzip_command2" => { type: "gzip_command", input: "Tempfile", limit: 10, expected_error: true },
648
+ "normal_gzip_command" => { type: "gzip_command", input: "Tempfile", limit: 100, expected_error: false },
649
+ )
650
+ def test_decompression_size_limit(data)
651
+ store_type = data[:type]
652
+ input_type = data[:input]
653
+ limit = data[:limit]
654
+ setup_mocks
655
+
656
+ config = <<~CONF
657
+ #{CONFIG}
658
+ check_apikey_on_start false
659
+ store_as #{store_type}
660
+ format none
661
+ decompression_size_limit #{limit}
662
+ CONF
663
+
664
+ d = create_driver(config)
665
+
666
+ s3_object = stub(Object.new)
667
+ s3_response = stub(Object.new)
668
+ s3_response.body {
669
+ content = "#{'a'*10}\n#{'b'*10}\n"
670
+
671
+ # Switching between Tempfile and StringIO to cover both branches of the
672
+ # `io.respond_to?(:path)` condition in `extract_with_command`.
673
+ # This ensures that:
674
+ # 1. The StringIO route correctly uses TextExtractor to create a protected temporary file.
675
+ # 2. The Tempfile route correctly limits the output size during Open3.popen3 execution.
676
+ io = (input_type == "Tempfile") ? Tempfile.new : StringIO.new
677
+
678
+ case store_type
679
+ when "gzip", "gzip_command"
680
+ io.binmode
681
+ Zlib::GzipWriter.wrap(io) { |gz|
682
+ gz.write content
683
+ gz.finish
684
+ }
685
+ when "text"
686
+ io.write content
687
+ end
688
+
689
+ io.rewind
690
+ io
691
+ }
692
+ s3_object.get { s3_response }
693
+ @s3_bucket.object(anything).at_least(1) { s3_object }
694
+
695
+ body = {
696
+ "Records" => [
697
+ {
698
+ "s3" => {
699
+ "object" => {
700
+ "key" => "test_key"
701
+ }
702
+ }
703
+ }
704
+ ]
705
+ }
706
+ message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
707
+ @sqs_poller.get_messages(anything, anything) do |config, stats|
708
+ config.before_request.call(stats) if config.before_request
709
+ stats.request_count += 1
710
+ if stats.request_count >= 1
711
+ d.instance.instance_variable_set(:@running, false)
712
+ end
713
+ [message]
714
+ end
715
+ d.run
716
+
717
+ if data[:expected_error]
718
+ # Verify the protection mechanism: ensure SizeLimitError is logged.
719
+ assert_true d.logs.any? { |l| l.include?("Extracted data exceeds limit of #{limit} bytes") }
720
+ else
721
+ # Verify the normal execution path: ensure data is correctly extracted via Open3.popen3.
722
+ expected_records = [{ "message" => "#{'a'*10}\n" }, { "message" => "#{'b'*10}\n" }]
723
+ assert_equal(expected_records, d.events.map {|_tag, _time, record| record })
724
+ assert_false d.logs.any? { |l| l.include?("error_class") }
725
+ end
726
+ end
727
+
617
728
  def test_regexp_matching
618
729
  setup_mocks
619
730
  d = create_driver(CONFIG + "\ncheck_apikey_on_start false\nstore_as text\nformat none\nmatch_regexp .*_key?")
@@ -635,7 +746,7 @@ EOS
635
746
  }
636
747
  ]
637
748
  }
638
- message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
749
+ message = Struct::StubMessage.new(1, 1, JSON.generate(body))
639
750
  @sqs_poller.get_messages(anything, anything) do |config, stats|
640
751
  config.before_request.call(stats) if config.before_request
641
752
  stats.request_count += 1
@@ -664,7 +775,7 @@ EOS
664
775
  }
665
776
  ]
666
777
  }
667
- message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
778
+ message = Struct::StubMessage.new(1, 1, JSON.generate(body))
668
779
  @sqs_poller.get_messages(anything, anything) do |config, stats|
669
780
  config.before_request.call(stats) if config.before_request
670
781
  stats.request_count += 1
@@ -708,8 +819,8 @@ EOS
708
819
  }
709
820
  }
710
821
  }
711
-
712
- message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
822
+
823
+ message = Struct::StubMessage.new(1, 1, JSON.generate(body))
713
824
  @sqs_poller.get_messages(anything, anything) do |config, stats|
714
825
  config.before_request.call(stats) if config.before_request
715
826
  stats.request_count += 1
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
@@ -109,6 +134,18 @@ class S3OutputTest < Test::Unit::TestCase
109
134
  assert(e.is_a?(Fluent::ConfigError))
110
135
  end
111
136
 
137
+ data('level default' => nil,
138
+ 'level 1' => 1)
139
+ def test_configure_with_mime_type_zstd(level)
140
+ conf = CONFIG.clone
141
+ conf << "\nstore_as zstd\n"
142
+ conf << "\n<compress>\nlevel #{level}\n</compress>\n" if level
143
+ d = create_driver(conf)
144
+ assert_equal 'zst', d.instance.instance_variable_get(:@compressor).ext
145
+ assert_equal 'application/x-zst', d.instance.instance_variable_get(:@compressor).content_type
146
+ assert_equal (level || 3), d.instance.instance_variable_get(:@compressor).instance_variable_get(:@compress_config).level
147
+ end
148
+
112
149
  def test_configure_with_path_style
113
150
  conf = CONFIG.clone
114
151
  conf << "\nforce_path_style true\n"
@@ -456,6 +493,152 @@ EOC
456
493
  FileUtils.rm_f(s3_local_file_path)
457
494
  end
458
495
 
496
+ def test_write_with_zstd
497
+ setup_mocks(true)
498
+ s3_local_file_path = "/tmp/s3-test.zst"
499
+
500
+ expected_s3path = "log/events/ts=20110102-13/events_0-#{Socket.gethostname}.zst"
501
+
502
+ setup_s3_object_mocks(s3_local_file_path: s3_local_file_path, s3path: expected_s3path)
503
+
504
+ config = CONFIG_TIME_SLICE + "\nstore_as zstd\n"
505
+ d = create_time_sliced_driver(config)
506
+
507
+ time = event_time("2011-01-02 13:14:15 UTC")
508
+ d.run(default_tag: "test") do
509
+ d.feed(time, { "a" => 1 })
510
+ d.feed(time, { "a" => 2 })
511
+ end
512
+
513
+ File.open(s3_local_file_path, 'rb') do |file|
514
+ compressed_data = file.read
515
+ uncompressed_data = Zstd.decompress(compressed_data)
516
+ expected_data = %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] +
517
+ %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n]
518
+ assert_equal expected_data, uncompressed_data
519
+ end
520
+ FileUtils.rm_f(s3_local_file_path)
521
+ end
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
+
459
642
  class MockResponse
460
643
  attr_reader :data
461
644
 
@@ -764,6 +947,92 @@ EOC
764
947
  assert_equal(expected_credentials, credentials)
765
948
  end
766
949
 
950
+ def test_web_identity_credentials_with_region_and_sts_http_proxy
951
+ expected_credentials = Aws::Credentials.new("test_key", "test_secret")
952
+ expected_region = "ap-northeast-1"
953
+ expected_sts_http_proxy = 'http://example.com'
954
+ sts_client = Aws::STS::Client.new(region: expected_region, http_proxy: expected_sts_http_proxy)
955
+ mock(Aws::STS::Client).new(region:expected_region, http_proxy: expected_sts_http_proxy){ sts_client }
956
+ mock(Aws::AssumeRoleWebIdentityCredentials).new({ role_arn: "test_arn",
957
+ role_session_name: "test_session",
958
+ web_identity_token_file: "test_file",
959
+ client: sts_client,
960
+ sts_http_proxy: expected_sts_http_proxy }){
961
+ expected_credentials
962
+ }
963
+ config = CONFIG_TIME_SLICE.split("\n").reject{|x| x =~ /.+aws_.+/}.join("\n")
964
+ config += %[
965
+ s3_region #{expected_region}
966
+ <web_identity_credentials>
967
+ role_arn test_arn
968
+ role_session_name test_session
969
+ web_identity_token_file test_file
970
+ sts_http_proxy #{expected_sts_http_proxy}
971
+ </web_identity_credentials>
972
+ ]
973
+ d = create_time_sliced_driver(config)
974
+ assert_nothing_raised { d.run {} }
975
+ client = d.instance.instance_variable_get(:@s3).client
976
+ credentials = client.config.credentials
977
+ assert_equal(expected_credentials, credentials)
978
+ end
979
+
980
+ def test_web_identity_credentials_with_sts_http_proxy
981
+ expected_credentials = Aws::Credentials.new("test_key", "test_secret")
982
+ expected_sts_http_proxy = 'http://example.com'
983
+ sts_client = Aws::STS::Client.new(region: "us-east-1", http_proxy: expected_sts_http_proxy)
984
+ mock(Aws::STS::Client).new(region: "us-east-1", http_proxy: expected_sts_http_proxy){ sts_client }
985
+ mock(Aws::AssumeRoleWebIdentityCredentials).new({ role_arn: "test_arn",
986
+ role_session_name: "test_session",
987
+ web_identity_token_file: "test_file",
988
+ client: sts_client,
989
+ sts_http_proxy: expected_sts_http_proxy }){
990
+ expected_credentials
991
+ }
992
+ config = CONFIG_TIME_SLICE.split("\n").reject{|x| x =~ /.+aws_.+/}.join("\n")
993
+ config += %[
994
+ <web_identity_credentials>
995
+ role_arn test_arn
996
+ role_session_name test_session
997
+ web_identity_token_file test_file
998
+ sts_http_proxy #{expected_sts_http_proxy}
999
+ </web_identity_credentials>
1000
+ ]
1001
+ d = create_time_sliced_driver(config)
1002
+ assert_nothing_raised { d.run {} }
1003
+ client = d.instance.instance_variable_get(:@s3).client
1004
+ credentials = client.config.credentials
1005
+ assert_equal(expected_credentials, credentials)
1006
+ end
1007
+
1008
+ def test_web_identity_credentials_with_sts_endpoint_url
1009
+ expected_credentials = Aws::Credentials.new("test_key", "test_secret")
1010
+ expected_sts_endpoint_url = 'http://example.com'
1011
+ sts_client = Aws::STS::Client.new(region: "us-east-1", endpoint: expected_sts_endpoint_url)
1012
+ mock(Aws::STS::Client).new(region: "us-east-1", endpoint: expected_sts_endpoint_url){ sts_client }
1013
+ mock(Aws::AssumeRoleWebIdentityCredentials).new({ role_arn: "test_arn",
1014
+ role_session_name: "test_session",
1015
+ web_identity_token_file: "test_file",
1016
+ client: sts_client,
1017
+ sts_endpoint_url: expected_sts_endpoint_url }){
1018
+ expected_credentials
1019
+ }
1020
+ config = CONFIG_TIME_SLICE.split("\n").reject{|x| x =~ /.+aws_.+/}.join("\n")
1021
+ config += %[
1022
+ <web_identity_credentials>
1023
+ role_arn test_arn
1024
+ role_session_name test_session
1025
+ web_identity_token_file test_file
1026
+ sts_endpoint_url #{expected_sts_endpoint_url}
1027
+ </web_identity_credentials>
1028
+ ]
1029
+ d = create_time_sliced_driver(config)
1030
+ assert_nothing_raised { d.run {} }
1031
+ client = d.instance.instance_variable_get(:@s3).client
1032
+ credentials = client.config.credentials
1033
+ assert_equal(expected_credentials, credentials)
1034
+ end
1035
+
767
1036
  def test_web_identity_credentials_with_sts_region
768
1037
  expected_credentials = Aws::Credentials.new("test_key", "test_secret")
769
1038
  sts_client = Aws::STS::Client.new(region: 'us-east-1')