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 +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.yaml +1 -0
- data/.github/ISSUE_TEMPLATE/feature_request.yaml +1 -0
- data/.github/dependabot.yml +23 -0
- data/.github/workflows/linux.yml +13 -4
- data/.github/workflows/stale-actions.yml +14 -9
- data/ChangeLog +30 -0
- data/README.md +1 -2
- data/VERSION +1 -1
- data/docs/credentials.md +18 -1
- data/docs/input.md +5 -0
- data/docs/output.md +14 -1
- data/fluent-plugin-s3.gemspec +2 -0
- data/lib/fluent/plugin/in_s3.rb +73 -11
- data/lib/fluent/plugin/out_s3.rb +44 -7
- data/lib/fluent/plugin/s3_compressor_gzip_command.rb +5 -0
- data/lib/fluent/plugin/s3_compressor_zstd.rb +41 -0
- data/lib/fluent/plugin/s3_extractor_gzip_command.rb +9 -20
- data/lib/fluent/plugin/s3_extractor_lzma2.rb +5 -13
- data/lib/fluent/plugin/s3_extractor_lzo.rb +5 -13
- data/test/test_in_s3.rb +123 -12
- data/test/test_out_s3.rb +269 -0
- metadata +36 -7
|
@@ -19,26 +19,15 @@ module Fluent::Plugin
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def extract(io)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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')
|