fluent-plugin-s3-file-inclusion 1.4.1
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 +7 -0
- data/.gitignore +13 -0
- data/.travis.yml +24 -0
- data/AUTHORS +2 -0
- data/ChangeLog +375 -0
- data/Gemfile +3 -0
- data/Gemfile.v0.12 +6 -0
- data/README.md +845 -0
- data/Rakefile +14 -0
- data/VERSION +1 -0
- data/appveyor.yml +25 -0
- data/fluent-plugin-s3.gemspec +26 -0
- data/lib/fluent/log-ext.rb +12 -0
- data/lib/fluent/plugin/in_s3.rb +419 -0
- data/lib/fluent/plugin/out_s3.rb +642 -0
- data/lib/fluent/plugin/s3_compressor_gzip_command.rb +52 -0
- data/lib/fluent/plugin/s3_compressor_lzma2.rb +35 -0
- data/lib/fluent/plugin/s3_compressor_lzo.rb +35 -0
- data/lib/fluent/plugin/s3_extractor_gzip_command.rb +46 -0
- data/lib/fluent/plugin/s3_extractor_lzma2.rb +40 -0
- data/lib/fluent/plugin/s3_extractor_lzo.rb +40 -0
- data/test/test_in_s3.rb +513 -0
- data/test/test_out_s3.rb +713 -0
- metadata +169 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
module Fluent::Plugin
|
2
|
+
class S3Output
|
3
|
+
class GzipCommandCompressor < Compressor
|
4
|
+
S3Output.register_compressor('gzip_command', self)
|
5
|
+
|
6
|
+
config_param :command_parameter, :string, default: ''
|
7
|
+
|
8
|
+
def configure(conf)
|
9
|
+
super
|
10
|
+
check_command('gzip')
|
11
|
+
end
|
12
|
+
|
13
|
+
def ext
|
14
|
+
'gz'.freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
def content_type
|
18
|
+
'application/x-gzip'.freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
def compress(chunk, tmp)
|
22
|
+
chunk_is_file = @buffer_type == 'file'
|
23
|
+
path = if chunk_is_file
|
24
|
+
chunk.path
|
25
|
+
else
|
26
|
+
w = Tempfile.new("chunk-gzip-tmp")
|
27
|
+
w.binmode
|
28
|
+
chunk.write_to(w)
|
29
|
+
w.close
|
30
|
+
w.path
|
31
|
+
end
|
32
|
+
|
33
|
+
res = system "gzip #{@command_parameter} -c #{path} > #{tmp.path}"
|
34
|
+
unless res
|
35
|
+
log.warn "failed to execute gzip command. Fallback to GzipWriter. status = #{$?}"
|
36
|
+
begin
|
37
|
+
tmp.truncate(0)
|
38
|
+
gw = Zlib::GzipWriter.new(tmp)
|
39
|
+
chunk.write_to(gw)
|
40
|
+
gw.close
|
41
|
+
ensure
|
42
|
+
gw.close rescue nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
ensure
|
46
|
+
unless chunk_is_file
|
47
|
+
w.close(true) rescue nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Fluent::Plugin
|
2
|
+
class S3Output
|
3
|
+
class LZMA2Compressor < Compressor
|
4
|
+
S3Output.register_compressor('lzma2', self)
|
5
|
+
|
6
|
+
config_param :command_parameter, :string, default: '-qf0'
|
7
|
+
|
8
|
+
def configure(conf)
|
9
|
+
super
|
10
|
+
check_command('xz', 'LZMA2')
|
11
|
+
end
|
12
|
+
|
13
|
+
def ext
|
14
|
+
'xz'.freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
def content_type
|
18
|
+
'application/x-xz'.freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
def compress(chunk, tmp)
|
22
|
+
w = Tempfile.new("chunk-xz-tmp")
|
23
|
+
w.binmode
|
24
|
+
chunk.write_to(w)
|
25
|
+
w.close
|
26
|
+
|
27
|
+
# We don't check the return code because we can't recover lzop failure.
|
28
|
+
system "xz #{@command_parameter} -c #{w.path} > #{tmp.path}"
|
29
|
+
ensure
|
30
|
+
w.close rescue nil
|
31
|
+
w.unlink rescue nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Fluent::Plugin
|
2
|
+
class S3Output
|
3
|
+
class LZOCompressor < Compressor
|
4
|
+
S3Output.register_compressor('lzo', self)
|
5
|
+
|
6
|
+
config_param :command_parameter, :string, default: '-qf1'
|
7
|
+
|
8
|
+
def configure(conf)
|
9
|
+
super
|
10
|
+
check_command('lzop', 'LZO')
|
11
|
+
end
|
12
|
+
|
13
|
+
def ext
|
14
|
+
'lzo'.freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
def content_type
|
18
|
+
'application/x-lzop'.freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
def compress(chunk, tmp)
|
22
|
+
w = Tempfile.new("chunk-tmp")
|
23
|
+
w.binmode
|
24
|
+
chunk.write_to(w)
|
25
|
+
w.close
|
26
|
+
|
27
|
+
# We don't check the return code because we can't recover lzop failure.
|
28
|
+
system "lzop #{@command_parameter} -o #{tmp.path} #{w.path}"
|
29
|
+
ensure
|
30
|
+
w.close rescue nil
|
31
|
+
w.unlink rescue nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Fluent::Plugin
|
2
|
+
class S3Input
|
3
|
+
class GzipCommandExtractor < Extractor
|
4
|
+
S3Input.register_extractor('gzip_command', self)
|
5
|
+
|
6
|
+
config_param :command_parameter, :string, default: '-dc'
|
7
|
+
|
8
|
+
def configure(conf)
|
9
|
+
super
|
10
|
+
check_command('gzip')
|
11
|
+
end
|
12
|
+
|
13
|
+
def ext
|
14
|
+
'gz'.freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
def content_type
|
18
|
+
'application/x-gzip'.freeze
|
19
|
+
end
|
20
|
+
|
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
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Fluent::Plugin
|
2
|
+
class S3Input
|
3
|
+
class LZMA2Extractor < Extractor
|
4
|
+
S3Input.register_extractor('lzma2', self)
|
5
|
+
|
6
|
+
config_param :command_parameter, :string, default: '-qdc'
|
7
|
+
|
8
|
+
def configure(conf)
|
9
|
+
super
|
10
|
+
check_command('xz', 'LZMA')
|
11
|
+
end
|
12
|
+
|
13
|
+
def ext
|
14
|
+
'xz'.freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
def content_type
|
18
|
+
'application/x-xz'.freeze
|
19
|
+
end
|
20
|
+
|
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
|
35
|
+
raise "Failed to extract #{path} with xz command."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Fluent::Plugin
|
2
|
+
class S3Input
|
3
|
+
class LZOExtractor < Extractor
|
4
|
+
S3Input.register_extractor('lzo', self)
|
5
|
+
|
6
|
+
config_param :command_parameter, :string, default: '-qdc'
|
7
|
+
|
8
|
+
def configure(conf)
|
9
|
+
super
|
10
|
+
check_command('lzop', 'LZO')
|
11
|
+
end
|
12
|
+
|
13
|
+
def ext
|
14
|
+
'lzo'.freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
def content_type
|
18
|
+
'application/x-lzop'.freeze
|
19
|
+
end
|
20
|
+
|
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
|
35
|
+
raise "Failed to extract #{path} with lzop command."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/test/test_in_s3.rb
ADDED
@@ -0,0 +1,513 @@
|
|
1
|
+
require 'aws-sdk-s3'
|
2
|
+
require 'aws-sdk-sqs'
|
3
|
+
require 'aws-sdk-sqs/queue_poller'
|
4
|
+
|
5
|
+
require 'fluent/test'
|
6
|
+
require 'fluent/test/helpers'
|
7
|
+
require 'fluent/test/log'
|
8
|
+
require 'fluent/test/driver/input'
|
9
|
+
require 'fluent/plugin/in_s3'
|
10
|
+
|
11
|
+
require 'test/unit/rr'
|
12
|
+
require 'zlib'
|
13
|
+
require 'fileutils'
|
14
|
+
require 'ostruct'
|
15
|
+
|
16
|
+
include Fluent::Test::Helpers
|
17
|
+
|
18
|
+
class S3InputTest < Test::Unit::TestCase
|
19
|
+
def setup
|
20
|
+
Fluent::Test.setup
|
21
|
+
@time = event_time("2015-09-30 13:14:15 UTC")
|
22
|
+
Fluent::Engine.now = @time
|
23
|
+
if Fluent.const_defined?(:EventTime)
|
24
|
+
stub(Fluent::EventTime).now { @time }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
CONFIG = %[
|
29
|
+
aws_key_id test_key_id
|
30
|
+
aws_sec_key test_sec_key
|
31
|
+
s3_bucket test_bucket
|
32
|
+
buffer_type memory
|
33
|
+
<sqs>
|
34
|
+
queue_name test_queue
|
35
|
+
queue_owner_aws_account_id 123456789123
|
36
|
+
</sqs>
|
37
|
+
]
|
38
|
+
|
39
|
+
def create_driver(conf = CONFIG)
|
40
|
+
Fluent::Test::Driver::Input.new(Fluent::Plugin::S3Input).configure(conf)
|
41
|
+
end
|
42
|
+
|
43
|
+
class ConfigTest < self
|
44
|
+
def test_default
|
45
|
+
d = create_driver
|
46
|
+
extractor = d.instance.instance_variable_get(:@extractor)
|
47
|
+
actual = {
|
48
|
+
aws_key_id: d.instance.aws_key_id,
|
49
|
+
aws_sec_key: d.instance.aws_sec_key,
|
50
|
+
s3_bucket: d.instance.s3_bucket,
|
51
|
+
s3_region: d.instance.s3_region,
|
52
|
+
sqs_queue_name: d.instance.sqs.queue_name,
|
53
|
+
extractor_ext: extractor.ext,
|
54
|
+
extractor_content_type: extractor.content_type
|
55
|
+
}
|
56
|
+
expected = {
|
57
|
+
aws_key_id: "test_key_id",
|
58
|
+
aws_sec_key: "test_sec_key",
|
59
|
+
s3_bucket: "test_bucket",
|
60
|
+
s3_region: "us-east-1",
|
61
|
+
sqs_queue_name: "test_queue",
|
62
|
+
extractor_ext: "gz",
|
63
|
+
extractor_content_type: "application/x-gzip"
|
64
|
+
}
|
65
|
+
assert_equal(expected, actual)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_empty
|
69
|
+
assert_raise(Fluent::ConfigError) do
|
70
|
+
create_driver("")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_without_sqs_section
|
75
|
+
conf = %[
|
76
|
+
aws_key_id test_key_id
|
77
|
+
aws_sec_key test_sec_key
|
78
|
+
s3_bucket test_bucket
|
79
|
+
]
|
80
|
+
assert_raise_message("'<sqs>' sections are required") do
|
81
|
+
create_driver(conf)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_unknown_store_as
|
86
|
+
config = CONFIG + "\nstore_as unknown"
|
87
|
+
assert_raise(Fluent::ConfigError) do
|
88
|
+
create_driver(config)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
data("json" => ["json", "json", "application/json"],
|
93
|
+
"text" => ["text", "txt", "text/plain"],
|
94
|
+
"gzip" => ["gzip", "gz", "application/x-gzip"],
|
95
|
+
"gzip_command" => ["gzip_command", "gz", "application/x-gzip"],
|
96
|
+
"lzo" => ["lzo", "lzo", "application/x-lzop"],
|
97
|
+
"lzma2" => ["lzma2", "xz", "application/x-xz"])
|
98
|
+
def test_extractor(data)
|
99
|
+
store_type, ext, content_type = data
|
100
|
+
config = CONFIG + "\nstore_as #{store_type}\n"
|
101
|
+
d = create_driver(config)
|
102
|
+
extractor = d.instance.instance_variable_get(:@extractor)
|
103
|
+
expected = {
|
104
|
+
ext: ext,
|
105
|
+
content_type: content_type
|
106
|
+
}
|
107
|
+
actual = {
|
108
|
+
ext: extractor.ext,
|
109
|
+
content_type: extractor.content_type
|
110
|
+
}
|
111
|
+
assert_equal(expected, actual)
|
112
|
+
rescue Fluent::ConfigError => e
|
113
|
+
pend(e.message)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def test_s3_endpoint_with_valid_endpoint
|
119
|
+
d = create_driver(CONFIG + 's3_endpoint riak-cs.example.com')
|
120
|
+
assert_equal 'riak-cs.example.com', d.instance.s3_endpoint
|
121
|
+
end
|
122
|
+
|
123
|
+
data('US West (Oregon)' => 's3-us-west-2.amazonaws.com',
|
124
|
+
'EU (Frankfurt)' => 's3.eu-central-1.amazonaws.com',
|
125
|
+
'Asia Pacific (Tokyo)' => 's3-ap-northeast-1.amazonaws.com')
|
126
|
+
def test_s3_endpoint_with_invalid_endpoint(endpoint)
|
127
|
+
assert_raise(Fluent::ConfigError, "s3_endpoint parameter is not supported, use s3_region instead. This parameter is for S3 compatible services") {
|
128
|
+
create_driver(CONFIG + "s3_endpoint #{endpoint}")
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
data('US West (Oregon)' => 's3-us-west-2.amazonaws.com',
|
133
|
+
'EU (Frankfurt)' => 's3.eu-central-1.amazonaws.com',
|
134
|
+
'Asia Pacific (Tokyo)' => 's3-ap-northeast-1.amazonaws.com')
|
135
|
+
def test_sqs_endpoint_with_invalid_endpoint(endpoint)
|
136
|
+
assert_raise(Fluent::ConfigError, "sqs.endpoint parameter is not supported, use s3_region instead. This parameter is for SQS compatible services") {
|
137
|
+
conf = <<"EOS"
|
138
|
+
aws_key_id test_key_id
|
139
|
+
aws_sec_key test_sec_key
|
140
|
+
s3_bucket test_bucket
|
141
|
+
buffer_type memory
|
142
|
+
<sqs>
|
143
|
+
queue_name test_queue
|
144
|
+
endpoint #{endpoint}
|
145
|
+
</sqs>
|
146
|
+
EOS
|
147
|
+
create_driver(conf)
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
151
|
+
Struct.new("StubResponse", :queue_url)
|
152
|
+
Struct.new("StubMessage", :message_id, :receipt_handle, :body)
|
153
|
+
|
154
|
+
def setup_mocks
|
155
|
+
@s3_client = stub(Aws::S3::Client.new(stub_responses: true))
|
156
|
+
stub(@s3_client).config { OpenStruct.new({region: "us-east-1"}) }
|
157
|
+
mock(Aws::S3::Client).new(anything).at_least(0) { @s3_client }
|
158
|
+
@s3_resource = mock(Aws::S3::Resource.new(client: @s3_client))
|
159
|
+
mock(Aws::S3::Resource).new(client: @s3_client) { @s3_resource }
|
160
|
+
@s3_bucket = mock(Aws::S3::Bucket.new(name: "test",
|
161
|
+
client: @s3_client))
|
162
|
+
@s3_bucket.exists? { true }
|
163
|
+
@s3_resource.bucket(anything) { @s3_bucket }
|
164
|
+
|
165
|
+
test_queue_url = "http://example.com/test_queue"
|
166
|
+
@sqs_client = stub(Aws::SQS::Client.new(stub_responses: true))
|
167
|
+
@sqs_response = stub(Struct::StubResponse.new(test_queue_url))
|
168
|
+
@sqs_client.get_queue_url(queue_name: "test_queue", queue_owner_aws_account_id: "123456789123"){ @sqs_response }
|
169
|
+
mock(Aws::SQS::Client).new(anything).once { @sqs_client }
|
170
|
+
@real_poller = Aws::SQS::QueuePoller.new(test_queue_url, client: @sqs_client)
|
171
|
+
@sqs_poller = stub(@real_poller)
|
172
|
+
mock(Aws::SQS::QueuePoller).new(anything, client: @sqs_client) { @sqs_poller }
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_no_records
|
176
|
+
setup_mocks
|
177
|
+
d = create_driver(CONFIG + "\ncheck_apikey_on_start false\n")
|
178
|
+
mock(d.instance).process(anything).never
|
179
|
+
|
180
|
+
message = Struct::StubMessage.new(1, 1, "{}")
|
181
|
+
@sqs_poller.get_messages(anything, anything) do |config, stats|
|
182
|
+
config.before_request.call(stats) if config.before_request
|
183
|
+
stats.request_count += 1
|
184
|
+
if stats.request_count > 1
|
185
|
+
d.instance.instance_variable_set(:@running, false)
|
186
|
+
end
|
187
|
+
[message]
|
188
|
+
end
|
189
|
+
assert_nothing_raised do
|
190
|
+
d.run {}
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def test_one_record
|
195
|
+
setup_mocks
|
196
|
+
d = create_driver(CONFIG + "\ncheck_apikey_on_start false\nstore_as text\nformat none\n")
|
197
|
+
|
198
|
+
s3_object = stub(Object.new)
|
199
|
+
s3_response = stub(Object.new)
|
200
|
+
s3_response.body { StringIO.new("aaa") }
|
201
|
+
s3_object.get { s3_response }
|
202
|
+
@s3_bucket.object(anything).at_least(1) { s3_object }
|
203
|
+
|
204
|
+
body = {
|
205
|
+
"Records" => [
|
206
|
+
{
|
207
|
+
"s3" => {
|
208
|
+
"object" => {
|
209
|
+
"key" => "test_key"
|
210
|
+
}
|
211
|
+
}
|
212
|
+
}
|
213
|
+
]
|
214
|
+
}
|
215
|
+
message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
|
216
|
+
@sqs_poller.get_messages(anything, anything) do |config, stats|
|
217
|
+
config.before_request.call(stats) if config.before_request
|
218
|
+
stats.request_count += 1
|
219
|
+
if stats.request_count >= 1
|
220
|
+
d.instance.instance_variable_set(:@running, false)
|
221
|
+
end
|
222
|
+
[message]
|
223
|
+
end
|
224
|
+
d.run(expect_emits: 1)
|
225
|
+
events = d.events
|
226
|
+
assert_equal({ "message" => "aaa" }, events.first[2])
|
227
|
+
end
|
228
|
+
|
229
|
+
def test_one_record_with_metadata
|
230
|
+
setup_mocks
|
231
|
+
d = create_driver(CONFIG + "\ncheck_apikey_on_start false\nstore_as text\nformat none\nadd_object_metadata true\n")
|
232
|
+
|
233
|
+
s3_object = stub(Object.new)
|
234
|
+
s3_response = stub(Object.new)
|
235
|
+
s3_response.body { StringIO.new("aaa") }
|
236
|
+
s3_object.get { s3_response }
|
237
|
+
@s3_bucket.object(anything).at_least(1) { s3_object }
|
238
|
+
|
239
|
+
body = {
|
240
|
+
"Records" => [
|
241
|
+
{
|
242
|
+
"s3" => {
|
243
|
+
"object" => {
|
244
|
+
"key" => "test_key"
|
245
|
+
}
|
246
|
+
}
|
247
|
+
}
|
248
|
+
]
|
249
|
+
}
|
250
|
+
message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
|
251
|
+
@sqs_poller.get_messages(anything, anything) do |config, stats|
|
252
|
+
config.before_request.call(stats) if config.before_request
|
253
|
+
stats.request_count += 1
|
254
|
+
if stats.request_count >= 1
|
255
|
+
d.instance.instance_variable_set(:@running, false)
|
256
|
+
end
|
257
|
+
[message]
|
258
|
+
end
|
259
|
+
d.run(expect_emits: 1)
|
260
|
+
events = d.events
|
261
|
+
assert_equal({ "s3_bucket" => "test_bucket", "s3_key" => "test_key", "message" => "aaa" }, events.first[2])
|
262
|
+
end
|
263
|
+
|
264
|
+
def test_one_record_url_encoded
|
265
|
+
setup_mocks
|
266
|
+
d = create_driver(CONFIG + "\ncheck_apikey_on_start false\nstore_as text\nformat none\n")
|
267
|
+
|
268
|
+
s3_object = stub(Object.new)
|
269
|
+
s3_response = stub(Object.new)
|
270
|
+
s3_response.body { StringIO.new("aaa") }
|
271
|
+
s3_object.get { s3_response }
|
272
|
+
@s3_bucket.object('test key').at_least(1) { s3_object }
|
273
|
+
|
274
|
+
body = {
|
275
|
+
"Records" => [
|
276
|
+
{
|
277
|
+
"s3" => {
|
278
|
+
"object" => {
|
279
|
+
"key" => "test+key"
|
280
|
+
}
|
281
|
+
}
|
282
|
+
}
|
283
|
+
]
|
284
|
+
}
|
285
|
+
message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
|
286
|
+
@sqs_poller.get_messages(anything, anything) do |config, stats|
|
287
|
+
config.before_request.call(stats) if config.before_request
|
288
|
+
stats.request_count += 1
|
289
|
+
if stats.request_count >= 1
|
290
|
+
d.instance.instance_variable_set(:@running, false)
|
291
|
+
end
|
292
|
+
[message]
|
293
|
+
end
|
294
|
+
d.run(expect_emits: 1)
|
295
|
+
events = d.events
|
296
|
+
assert_equal({ "message" => "aaa" }, events.first[2])
|
297
|
+
end
|
298
|
+
|
299
|
+
def test_one_record_url_encoded_with_metadata
|
300
|
+
setup_mocks
|
301
|
+
d = create_driver(CONFIG + "\ncheck_apikey_on_start false\nstore_as text\nformat none\nadd_object_metadata true")
|
302
|
+
|
303
|
+
s3_object = stub(Object.new)
|
304
|
+
s3_response = stub(Object.new)
|
305
|
+
s3_response.body { StringIO.new("aaa") }
|
306
|
+
s3_object.get { s3_response }
|
307
|
+
@s3_bucket.object('test key').at_least(1) { s3_object }
|
308
|
+
|
309
|
+
body = {
|
310
|
+
"Records" => [
|
311
|
+
{
|
312
|
+
"s3" => {
|
313
|
+
"object" => {
|
314
|
+
"key" => "test+key"
|
315
|
+
}
|
316
|
+
}
|
317
|
+
}
|
318
|
+
]
|
319
|
+
}
|
320
|
+
message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
|
321
|
+
@sqs_poller.get_messages(anything, anything) do |config, stats|
|
322
|
+
config.before_request.call(stats) if config.before_request
|
323
|
+
stats.request_count += 1
|
324
|
+
if stats.request_count >= 1
|
325
|
+
d.instance.instance_variable_set(:@running, false)
|
326
|
+
end
|
327
|
+
[message]
|
328
|
+
end
|
329
|
+
d.run(expect_emits: 1)
|
330
|
+
events = d.events
|
331
|
+
assert_equal({ "s3_bucket" => "test_bucket", "s3_key" => "test+key", "message" => "aaa" }, events.first[2])
|
332
|
+
end
|
333
|
+
|
334
|
+
def test_one_record_multi_line
|
335
|
+
setup_mocks
|
336
|
+
d = create_driver(CONFIG + "\ncheck_apikey_on_start false\nstore_as text\nformat none\n")
|
337
|
+
|
338
|
+
s3_object = stub(Object.new)
|
339
|
+
s3_response = stub(Object.new)
|
340
|
+
s3_response.body { StringIO.new("aaa\nbbb\nccc\n") }
|
341
|
+
s3_object.get { s3_response }
|
342
|
+
@s3_bucket.object(anything).at_least(1) { s3_object }
|
343
|
+
|
344
|
+
body = {
|
345
|
+
"Records" => [
|
346
|
+
{
|
347
|
+
"s3" => {
|
348
|
+
"object" => {
|
349
|
+
"key" => "test_key"
|
350
|
+
}
|
351
|
+
}
|
352
|
+
}
|
353
|
+
]
|
354
|
+
}
|
355
|
+
message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
|
356
|
+
@sqs_poller.get_messages(anything, anything) do |config, stats|
|
357
|
+
config.before_request.call(stats) if config.before_request
|
358
|
+
stats.request_count += 1
|
359
|
+
if stats.request_count >= 1
|
360
|
+
d.instance.instance_variable_set(:@running, false)
|
361
|
+
end
|
362
|
+
[message]
|
363
|
+
end
|
364
|
+
d.run(expect_emits: 1)
|
365
|
+
events = d.events
|
366
|
+
expected_records = [
|
367
|
+
{ "message" => "aaa\n" },
|
368
|
+
{ "message" => "bbb\n" },
|
369
|
+
{ "message" => "ccc\n" }
|
370
|
+
]
|
371
|
+
assert_equal(expected_records, events.map {|_tag, _time, record| record })
|
372
|
+
end
|
373
|
+
|
374
|
+
def test_one_record_multi_line_with_metadata
|
375
|
+
setup_mocks
|
376
|
+
d = create_driver(CONFIG + "\ncheck_apikey_on_start false\nstore_as text\nformat none\nadd_object_metadata true")
|
377
|
+
|
378
|
+
s3_object = stub(Object.new)
|
379
|
+
s3_response = stub(Object.new)
|
380
|
+
s3_response.body { StringIO.new("aaa\nbbb\nccc\n") }
|
381
|
+
s3_object.get { s3_response }
|
382
|
+
@s3_bucket.object(anything).at_least(1) { s3_object }
|
383
|
+
|
384
|
+
body = {
|
385
|
+
"Records" => [
|
386
|
+
{
|
387
|
+
"s3" => {
|
388
|
+
"object" => {
|
389
|
+
"key" => "test_key"
|
390
|
+
}
|
391
|
+
}
|
392
|
+
}
|
393
|
+
]
|
394
|
+
}
|
395
|
+
message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
|
396
|
+
@sqs_poller.get_messages(anything, anything) do |config, stats|
|
397
|
+
config.before_request.call(stats) if config.before_request
|
398
|
+
stats.request_count += 1
|
399
|
+
if stats.request_count >= 1
|
400
|
+
d.instance.instance_variable_set(:@running, false)
|
401
|
+
end
|
402
|
+
[message]
|
403
|
+
end
|
404
|
+
d.run(expect_emits: 1)
|
405
|
+
events = d.events
|
406
|
+
expected_records = [
|
407
|
+
{ "s3_bucket" => "test_bucket", "s3_key" => "test_key", "message" => "aaa\n" },
|
408
|
+
{ "s3_bucket" => "test_bucket", "s3_key" => "test_key", "message" => "bbb\n" },
|
409
|
+
{ "s3_bucket" => "test_bucket", "s3_key" => "test_key", "message" => "ccc\n" }
|
410
|
+
]
|
411
|
+
assert_equal(expected_records, events.map {|_tag, _time, record| record })
|
412
|
+
end
|
413
|
+
|
414
|
+
def test_gzip_single_stream
|
415
|
+
setup_mocks
|
416
|
+
d = create_driver(CONFIG + "\ncheck_apikey_on_start false\nstore_as gzip\nformat none\n")
|
417
|
+
|
418
|
+
s3_object = stub(Object.new)
|
419
|
+
s3_response = stub(Object.new)
|
420
|
+
s3_response.body {
|
421
|
+
io = StringIO.new
|
422
|
+
Zlib::GzipWriter.wrap(io) do |gz|
|
423
|
+
gz.write "aaa\nbbb\n"
|
424
|
+
gz.finish
|
425
|
+
end
|
426
|
+
io.rewind
|
427
|
+
io
|
428
|
+
}
|
429
|
+
s3_object.get { s3_response }
|
430
|
+
@s3_bucket.object(anything).at_least(1) { s3_object }
|
431
|
+
|
432
|
+
body = {
|
433
|
+
"Records" => [
|
434
|
+
{
|
435
|
+
"s3" => {
|
436
|
+
"object" => {
|
437
|
+
"key" => "test_key"
|
438
|
+
}
|
439
|
+
}
|
440
|
+
}
|
441
|
+
]
|
442
|
+
}
|
443
|
+
message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
|
444
|
+
@sqs_poller.get_messages(anything, anything) do |config, stats|
|
445
|
+
config.before_request.call(stats) if config.before_request
|
446
|
+
stats.request_count += 1
|
447
|
+
if stats.request_count >= 1
|
448
|
+
d.instance.instance_variable_set(:@running, false)
|
449
|
+
end
|
450
|
+
[message]
|
451
|
+
end
|
452
|
+
d.run(expect_emits: 1)
|
453
|
+
events = d.events
|
454
|
+
expected_records = [
|
455
|
+
{ "message" => "aaa\n" },
|
456
|
+
{ "message" => "bbb\n" }
|
457
|
+
]
|
458
|
+
assert_equal(expected_records, events.map {|_tag, _time, record| record })
|
459
|
+
end
|
460
|
+
|
461
|
+
def test_gzip_multiple_steams
|
462
|
+
setup_mocks
|
463
|
+
d = create_driver(CONFIG + "\ncheck_apikey_on_start false\nstore_as gzip\nformat none\n")
|
464
|
+
|
465
|
+
s3_object = stub(Object.new)
|
466
|
+
s3_response = stub(Object.new)
|
467
|
+
s3_response.body {
|
468
|
+
io = StringIO.new
|
469
|
+
Zlib::GzipWriter.wrap(io) do |gz|
|
470
|
+
gz.write "aaa\nbbb\n"
|
471
|
+
gz.finish
|
472
|
+
end
|
473
|
+
Zlib::GzipWriter.wrap(io) do |gz|
|
474
|
+
gz.write "ccc\nddd\n"
|
475
|
+
gz.finish
|
476
|
+
end
|
477
|
+
io.rewind
|
478
|
+
io
|
479
|
+
}
|
480
|
+
s3_object.get { s3_response }
|
481
|
+
@s3_bucket.object(anything).at_least(1) { s3_object }
|
482
|
+
|
483
|
+
body = {
|
484
|
+
"Records" => [
|
485
|
+
{
|
486
|
+
"s3" => {
|
487
|
+
"object" => {
|
488
|
+
"key" => "test_key"
|
489
|
+
}
|
490
|
+
}
|
491
|
+
}
|
492
|
+
]
|
493
|
+
}
|
494
|
+
message = Struct::StubMessage.new(1, 1, Yajl.dump(body))
|
495
|
+
@sqs_poller.get_messages(anything, anything) do |config, stats|
|
496
|
+
config.before_request.call(stats) if config.before_request
|
497
|
+
stats.request_count += 1
|
498
|
+
if stats.request_count >= 1
|
499
|
+
d.instance.instance_variable_set(:@running, false)
|
500
|
+
end
|
501
|
+
[message]
|
502
|
+
end
|
503
|
+
d.run(expect_emits: 1)
|
504
|
+
events = d.events
|
505
|
+
expected_records = [
|
506
|
+
{ "message" => "aaa\n" },
|
507
|
+
{ "message" => "bbb\n" },
|
508
|
+
{ "message" => "ccc\n" },
|
509
|
+
{ "message" => "ddd\n" }
|
510
|
+
]
|
511
|
+
assert_equal(expected_records, events.map {|_tag, _time, record| record })
|
512
|
+
end
|
513
|
+
end
|