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