fluent-plugin-s3 0.8.0.rc1 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|