fluent-plugin-s3 0.8.0.rc1 → 0.8.0
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/.travis.yml +3 -0
- data/ChangeLog +6 -0
- data/Gemfile +0 -2
- data/README.md +28 -0
- data/VERSION +1 -1
- data/lib/fluent/plugin/out_s3.rb +50 -24
- data/test/test_in_s3.rb +0 -2
- data/test/test_out_s3.rb +73 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa201801e95642044876a29fe308d0df2912c214
|
4
|
+
data.tar.gz: a04b2703154c2d7efd698911bb73a04e22ca0d8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 425e1f44dcf826ee015b0b6581ed580648419dab9562a7ea5b2afe5dbb25cd278767e4c0be2ccab3aeb9eaa233a86a92c7721a11c6a1c6487e0f8b302736c11e
|
7
|
+
data.tar.gz: 23c621c0922ad06e43766bc58c92e203f389c09b6b3a1d03c7ee4c3fa186368ae6032c9ed5cfa568bcde0f3ed2435257cc0353d7f0a5530abccc980132e3e861
|
data/.travis.yml
CHANGED
data/ChangeLog
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -209,6 +209,34 @@ You can change key name by "message_key" option.
|
|
209
209
|
|
210
210
|
Create S3 bucket if it does not exists. Default is true.
|
211
211
|
|
212
|
+
**check_bukcet**
|
213
|
+
|
214
|
+
Check mentioned bucket if it exists in AWS or not. Default is true.
|
215
|
+
|
216
|
+
When it is false,
|
217
|
+
fluentd will not check aws s3 for the existence of the mentioned bucket. This is the
|
218
|
+
case where bucket will be pre-created before running fluentd.
|
219
|
+
|
220
|
+
**check_object**
|
221
|
+
|
222
|
+
Check object before creation if it exists or not. Default is true.
|
223
|
+
|
224
|
+
When it is false,
|
225
|
+
s3_object_key_format will be %{path}%{date_slice}_%{time_slice}.%{file_extension}
|
226
|
+
where, time_slice will be in hhmmss format, so that each object will be unique.
|
227
|
+
Example object name, assuming it is created on 2016/16/11 3:30:54 PM
|
228
|
+
20161611_153054.txt (extension can be anything as per user's choice)
|
229
|
+
|
230
|
+
**Example when check_bukcet=false and check_object=false**
|
231
|
+
|
232
|
+
When the mentioned configuration will be made, fluentd will work with the
|
233
|
+
minimum IAM poilcy, like:
|
234
|
+
"Statement": [{
|
235
|
+
"Effect": "Allow",
|
236
|
+
"Action": "s3:PutObject",
|
237
|
+
"Resource": ["*"]
|
238
|
+
}]
|
239
|
+
|
212
240
|
**check_apikey_on_start**
|
213
241
|
|
214
242
|
Check AWS key on start. Default is true.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.8.0
|
1
|
+
0.8.0
|
data/lib/fluent/plugin/out_s3.rb
CHANGED
@@ -101,6 +101,10 @@ module Fluent
|
|
101
101
|
config_param :hex_random_length, :integer, :default => 4
|
102
102
|
desc "Overwrite already existing path"
|
103
103
|
config_param :overwrite, :bool, :default => false
|
104
|
+
desc "Check bucket if exists or not"
|
105
|
+
config_param :check_bucket, :bool, :default => true
|
106
|
+
desc "Check object before creation"
|
107
|
+
config_param :check_object, :bool, :default => true
|
104
108
|
desc "Specifies the AWS KMS key ID to use for object encryption"
|
105
109
|
config_param :ssekms_key_id, :string, :default => nil, :secret => true
|
106
110
|
desc "Specifies the algorithm to use to when encrypting the object"
|
@@ -175,7 +179,11 @@ module Fluent
|
|
175
179
|
@bucket = @s3.bucket(@s3_bucket)
|
176
180
|
|
177
181
|
check_apikeys if @check_apikey_on_start
|
178
|
-
ensure_bucket
|
182
|
+
ensure_bucket if @check_bucket
|
183
|
+
|
184
|
+
if !@check_object
|
185
|
+
@s3_object_key_format = "%{path}/%{date_slice}_%{hms_slice}.%{file_extension}"
|
186
|
+
end
|
179
187
|
|
180
188
|
super
|
181
189
|
end
|
@@ -188,35 +196,53 @@ module Fluent
|
|
188
196
|
i = 0
|
189
197
|
previous_path = nil
|
190
198
|
|
191
|
-
|
192
|
-
|
199
|
+
if @check_object
|
200
|
+
begin
|
201
|
+
path = @path_slicer.call(@path)
|
202
|
+
|
203
|
+
@values_for_s3_object_chunk[chunk.unique_id] ||= {
|
204
|
+
"hex_random" => hex_random(chunk),
|
205
|
+
}
|
206
|
+
values_for_s3_object_key = {
|
207
|
+
"path" => path,
|
208
|
+
"time_slice" => chunk.key,
|
209
|
+
"file_extension" => @compressor.ext,
|
210
|
+
"index" => i,
|
211
|
+
}.merge!(@values_for_s3_object_chunk[chunk.unique_id])
|
212
|
+
values_for_s3_object_key['uuid_flush'.freeze] = uuid_random if @uuid_flush_enabled
|
213
|
+
|
214
|
+
s3path = @s3_object_key_format.gsub(%r(%{[^}]+})) { |expr|
|
215
|
+
values_for_s3_object_key[expr[2...expr.size-1]]
|
216
|
+
}
|
217
|
+
if (i > 0) && (s3path == previous_path)
|
218
|
+
if @overwrite
|
219
|
+
log.warn "#{s3path} already exists, but will overwrite"
|
220
|
+
break
|
221
|
+
else
|
222
|
+
raise "duplicated path is generated. use %{index} in s3_object_key_format: path = #{s3path}"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
i += 1
|
227
|
+
previous_path = s3path
|
228
|
+
end while @bucket.object(s3path).exists?
|
229
|
+
else
|
230
|
+
if @localtime
|
231
|
+
hms_slicer = Time.now.strftime("%H%M%S")
|
232
|
+
else
|
233
|
+
hms_slicer = Time.now.utc.strftime("%H%M%S")
|
234
|
+
end
|
193
235
|
|
194
|
-
@values_for_s3_object_chunk[chunk.unique_id] ||= {
|
195
|
-
"hex_random" => hex_random(chunk),
|
196
|
-
}
|
197
236
|
values_for_s3_object_key = {
|
198
|
-
"path" => path,
|
199
|
-
"
|
237
|
+
"path" => @path_slicer.call(@path),
|
238
|
+
"date_slice" => chunk.key,
|
200
239
|
"file_extension" => @compressor.ext,
|
201
|
-
"
|
202
|
-
}
|
203
|
-
values_for_s3_object_key['uuid_flush'.freeze] = uuid_random if @uuid_flush_enabled
|
204
|
-
|
240
|
+
"hms_slice" => hms_slicer,
|
241
|
+
}
|
205
242
|
s3path = @s3_object_key_format.gsub(%r(%{[^}]+})) { |expr|
|
206
243
|
values_for_s3_object_key[expr[2...expr.size-1]]
|
207
244
|
}
|
208
|
-
|
209
|
-
if @overwrite
|
210
|
-
log.warn "#{s3path} already exists, but will overwrite"
|
211
|
-
break
|
212
|
-
else
|
213
|
-
raise "duplicated path is generated. use %{index} in s3_object_key_format: path = #{s3path}"
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
i += 1
|
218
|
-
previous_path = s3path
|
219
|
-
end while @bucket.object(s3path).exists?
|
245
|
+
end
|
220
246
|
|
221
247
|
tmp = Tempfile.new("s3-")
|
222
248
|
tmp.binmode
|
data/test/test_in_s3.rb
CHANGED
@@ -21,7 +21,6 @@ class S3InputTest < Test::Unit::TestCase
|
|
21
21
|
aws_key_id test_key_id
|
22
22
|
aws_sec_key test_sec_key
|
23
23
|
s3_bucket test_bucket
|
24
|
-
utc
|
25
24
|
buffer_type memory
|
26
25
|
<sqs>
|
27
26
|
queue_name test_queue
|
@@ -70,7 +69,6 @@ class S3InputTest < Test::Unit::TestCase
|
|
70
69
|
aws_key_id test_key_id
|
71
70
|
aws_sec_key test_sec_key
|
72
71
|
s3_bucket test_bucket
|
73
|
-
utc
|
74
72
|
]
|
75
73
|
assert_raise_message("'<sqs>' sections are required") do
|
76
74
|
create_driver(conf)
|
data/test/test_out_s3.rb
CHANGED
@@ -52,6 +52,8 @@ class S3OutputTest < Test::Unit::TestCase
|
|
52
52
|
assert_equal false, d.instance.force_path_style
|
53
53
|
assert_equal nil, d.instance.compute_checksums
|
54
54
|
assert_equal nil, d.instance.signature_version
|
55
|
+
assert_equal true, d.instance.check_bucket
|
56
|
+
assert_equal true, d.instance.check_object
|
55
57
|
end
|
56
58
|
|
57
59
|
def test_s3_endpoint_with_valid_endpoint
|
@@ -119,6 +121,14 @@ class S3OutputTest < Test::Unit::TestCase
|
|
119
121
|
end
|
120
122
|
end
|
121
123
|
|
124
|
+
def test_configure_with_no_check_on_s3
|
125
|
+
conf = CONFIG.clone
|
126
|
+
conf << "\ncheck_bucket false\ncheck_object false\n"
|
127
|
+
d = create_driver(conf)
|
128
|
+
assert_equal false, d.instance.check_bucket
|
129
|
+
assert_equal false, d.instance.check_object
|
130
|
+
end
|
131
|
+
|
122
132
|
def test_path_slicing
|
123
133
|
config = CONFIG.clone.gsub(/path\slog/, "path log/%Y/%m/%d")
|
124
134
|
d = create_driver(config)
|
@@ -260,6 +270,8 @@ class S3OutputTest < Test::Unit::TestCase
|
|
260
270
|
utc
|
261
271
|
buffer_type memory
|
262
272
|
log_level debug
|
273
|
+
check_bucket true
|
274
|
+
check_object true
|
263
275
|
]
|
264
276
|
|
265
277
|
def create_time_sliced_driver(conf = CONFIG_TIME_SLICE)
|
@@ -272,6 +284,37 @@ class S3OutputTest < Test::Unit::TestCase
|
|
272
284
|
d
|
273
285
|
end
|
274
286
|
|
287
|
+
def test_write_with_hardened_s3_policy
|
288
|
+
# Partial mock the S3Bucket, not to make an actual connection to Amazon S3
|
289
|
+
setup_mocks_hardened_policy
|
290
|
+
s3_local_file_path = "/tmp/s3-test.txt"
|
291
|
+
# @s3_object_key_format will be hard_coded with timestamp only,
|
292
|
+
# as in this case, it will not check for object existence, not even bucker existence
|
293
|
+
# check_bukcet and check_object both of this config parameter should be false
|
294
|
+
# @s3_object_key_format = "%{path}/%{time_slice}_%{hms_slice}.%{file_extension}"
|
295
|
+
setup_s3_object_mocks_hardened_policy()
|
296
|
+
|
297
|
+
# We must use TimeSlicedOutputTestDriver instead of BufferedOutputTestDriver,
|
298
|
+
# to make assertions on chunks' keys
|
299
|
+
config = CONFIG_TIME_SLICE.gsub(/check_object true/, "check_object false\n")
|
300
|
+
config = config.gsub(/check_bucket true/, "check_bucket false\n")
|
301
|
+
d = create_time_sliced_driver(config)
|
302
|
+
|
303
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
304
|
+
d.emit({"a"=>1}, time)
|
305
|
+
d.emit({"a"=>2}, time)
|
306
|
+
|
307
|
+
# Finally, the instance of S3Output is initialized and then invoked
|
308
|
+
d.run
|
309
|
+
Zlib::GzipReader.open(s3_local_file_path) do |gz|
|
310
|
+
data = gz.read
|
311
|
+
assert_equal %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n] +
|
312
|
+
%[2011-01-02T13:14:15Z\ttest\t{"a":2}\n],
|
313
|
+
data
|
314
|
+
end
|
315
|
+
FileUtils.rm_f(s3_local_file_path)
|
316
|
+
end
|
317
|
+
|
275
318
|
def test_write_with_custom_s3_object_key_format
|
276
319
|
# Partial mock the S3Bucket, not to make an actual connection to Amazon S3
|
277
320
|
setup_mocks(true)
|
@@ -404,6 +447,36 @@ class S3OutputTest < Test::Unit::TestCase
|
|
404
447
|
@s3_bucket.object(s3path) { s3obj }
|
405
448
|
end
|
406
449
|
|
450
|
+
def setup_mocks_hardened_policy()
|
451
|
+
@s3_client = stub(Aws::S3::Client.new(:stub_responses => true))
|
452
|
+
mock(Aws::S3::Client).new(anything).at_least(0) { @s3_client }
|
453
|
+
@s3_resource = mock(Aws::S3::Resource.new(:client => @s3_client))
|
454
|
+
mock(Aws::S3::Resource).new(:client => @s3_client) { @s3_resource }
|
455
|
+
@s3_bucket = mock(Aws::S3::Bucket.new(:name => "test",
|
456
|
+
:client => @s3_client))
|
457
|
+
@s3_object = mock(Aws::S3::Object.new(:bucket_name => "test_bucket",
|
458
|
+
:key => "test",
|
459
|
+
:client => @s3_client))
|
460
|
+
@s3_bucket.object(anything).at_least(0) { @s3_object }
|
461
|
+
@s3_resource.bucket(anything) { @s3_bucket }
|
462
|
+
end
|
463
|
+
|
464
|
+
def setup_s3_object_mocks_hardened_policy(params = {})
|
465
|
+
s3path = params[:s3path] || "log/20110102_131415.gz"
|
466
|
+
s3_local_file_path = params[:s3_local_file_path] || "/tmp/s3-test.txt"
|
467
|
+
|
468
|
+
# Assert content of event logs which are being sent to S3
|
469
|
+
s3obj = stub(Aws::S3::Object.new(:bucket_name => "test_bucket",
|
470
|
+
:key => "test",
|
471
|
+
:client => @s3_client))
|
472
|
+
|
473
|
+
tempfile = File.new(s3_local_file_path, "w")
|
474
|
+
stub(Tempfile).new("s3-") { tempfile }
|
475
|
+
s3obj.put(:body => tempfile,
|
476
|
+
:content_type => "application/x-gzip",
|
477
|
+
:storage_class => "STANDARD")
|
478
|
+
end
|
479
|
+
|
407
480
|
def test_auto_create_bucket_false_with_non_existence_bucket
|
408
481
|
setup_mocks
|
409
482
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-s3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.0
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sadayuki Furuhashi
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-12-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: fluentd
|
@@ -163,9 +163,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
163
163
|
version: '0'
|
164
164
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
165
|
requirements:
|
166
|
-
- - "
|
166
|
+
- - ">="
|
167
167
|
- !ruby/object:Gem::Version
|
168
|
-
version:
|
168
|
+
version: '0'
|
169
169
|
requirements: []
|
170
170
|
rubyforge_project:
|
171
171
|
rubygems_version: 2.5.1
|