logstash-input-s3 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/logstash/inputs/s3.rb +190 -170
- data/logstash-input-s3.gemspec +3 -2
- data/spec/inputs/s3_spec.rb +141 -0
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1be6737259a6ffb9f0f0734931ab2f495912bac4
|
4
|
+
data.tar.gz: f794cf9ce4f876bb3ecd81ab0f167cefc1fd692d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97dbbbda141e669cdad3bfce516764e649ccf929d72509d858e86211f772fb8f2e91311b945ff7576616a941f161afe41faa57769913b83e72b9697a6e073140
|
7
|
+
data.tar.gz: d6235bf2a17418da6108349144dfef750526eaa2405a09607e49db1748e76f88f5e14851a6384fbe06a4ae26cde4d7de2187a069aba3b6664c9c2e9fe3c25ef7
|
data/lib/logstash/inputs/s3.rb
CHANGED
@@ -1,44 +1,41 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require "logstash/inputs/base"
|
3
3
|
require "logstash/namespace"
|
4
|
+
require "logstash/plugin_mixins/aws_config"
|
4
5
|
|
5
6
|
require "time"
|
6
7
|
require "tmpdir"
|
8
|
+
require "stud/interval"
|
9
|
+
require "stud/temporary"
|
7
10
|
|
8
11
|
# Stream events from files from a S3 bucket.
|
9
12
|
#
|
10
13
|
# Each line from each file generates an event.
|
11
14
|
# Files ending in `.gz` are handled as gzip'ed files.
|
12
15
|
class LogStash::Inputs::S3 < LogStash::Inputs::Base
|
16
|
+
include LogStash::PluginMixins::AwsConfig
|
17
|
+
|
13
18
|
config_name "s3"
|
14
19
|
milestone 1
|
15
20
|
|
16
|
-
|
17
|
-
# support and readline usage). Support gzip through a gzip codec! ;)
|
18
|
-
default :codec, "plain"
|
21
|
+
default :codec, "line"
|
19
22
|
|
20
|
-
# The credentials of the AWS account used to access the bucket.
|
23
|
+
# DEPRECATED: The credentials of the AWS account used to access the bucket.
|
21
24
|
# Credentials can be specified:
|
22
|
-
# - As an
|
23
|
-
# - As a path to a file containing
|
24
|
-
# - In the environment, if not set (using variables
|
25
|
-
config :credentials, :validate => :array, :default => []
|
25
|
+
# - As an ["id","secret"] array
|
26
|
+
# - As a path to a file containing AWS_ACCESS_KEY_ID=... and AWS_SECRET_ACCESS_KEY=...
|
27
|
+
# - In the environment, if not set (using variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY)
|
28
|
+
config :credentials, :validate => :array, :default => [], :deprecated => "This only exists to be backwards compatible. This plugin now uses the AwsConfig from PluginMixins"
|
26
29
|
|
27
30
|
# The name of the S3 bucket.
|
28
31
|
config :bucket, :validate => :string, :required => true
|
29
32
|
|
30
|
-
# The AWS region for your bucket.
|
31
|
-
config :region, :validate => ["us-east-1", "us-west-1", "us-west-2",
|
32
|
-
"eu-west-1", "ap-southeast-1", "ap-southeast-2",
|
33
|
-
"ap-northeast-1", "sa-east-1", "us-gov-west-1"],
|
34
|
-
:deprecated => "'region' has been deprecated in favor of 'region_endpoint'"
|
35
|
-
|
36
33
|
# The AWS region for your bucket.
|
37
34
|
config :region_endpoint, :validate => ["us-east-1", "us-west-1", "us-west-2",
|
38
35
|
"eu-west-1", "ap-southeast-1", "ap-southeast-2",
|
39
|
-
"ap-northeast-1", "sa-east-1", "us-gov-west-1"], :
|
36
|
+
"ap-northeast-1", "sa-east-1", "us-gov-west-1"], :deprecated => "This only exists to be backwards compatible. This plugin now uses the AwsConfig from PluginMixins"
|
40
37
|
|
41
|
-
# If specified, the prefix
|
38
|
+
# If specified, the prefix of filenames in the bucket must match (not a regexp)
|
42
39
|
config :prefix, :validate => :string, :default => nil
|
43
40
|
|
44
41
|
# Where to write the since database (keeps track of the date
|
@@ -50,6 +47,11 @@ class LogStash::Inputs::S3 < LogStash::Inputs::Base
|
|
50
47
|
# Name of a S3 bucket to backup processed files to.
|
51
48
|
config :backup_to_bucket, :validate => :string, :default => nil
|
52
49
|
|
50
|
+
# Append a prefix to the key (full path including file name in s3) after processing.
|
51
|
+
# If backing up to another (or the same) bucket, this effectively lets you
|
52
|
+
# choose a new 'folder' to place the files in
|
53
|
+
config :backup_add_prefix, :validate => :string, :default => nil
|
54
|
+
|
53
55
|
# Path of a local directory to backup processed files to.
|
54
56
|
config :backup_to_dir, :validate => :string, :default => nil
|
55
57
|
|
@@ -60,61 +62,19 @@ class LogStash::Inputs::S3 < LogStash::Inputs::Base
|
|
60
62
|
# Value is in seconds.
|
61
63
|
config :interval, :validate => :number, :default => 60
|
62
64
|
|
65
|
+
# Ruby style regexp of keys to exclude from the bucket
|
66
|
+
config :exclude_pattern, :validate => :string, :default => nil
|
67
|
+
|
63
68
|
public
|
64
69
|
def register
|
65
70
|
require "digest/md5"
|
66
71
|
require "aws-sdk"
|
67
72
|
|
68
|
-
@
|
69
|
-
|
70
|
-
@logger.info("Registering s3 input", :bucket => @bucket, :region_endpoint => @region_endpoint)
|
71
|
-
|
72
|
-
if @credentials.length == 0
|
73
|
-
@access_key_id = ENV['AWS_ACCESS_KEY_ID']
|
74
|
-
@secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
|
75
|
-
elsif @credentials.length == 1
|
76
|
-
File.open(@credentials[0]) { |f| f.each do |line|
|
77
|
-
unless (/^\#/.match(line))
|
78
|
-
if(/\s*=\s*/.match(line))
|
79
|
-
param, value = line.split('=', 2)
|
80
|
-
param = param.chomp().strip()
|
81
|
-
value = value.chomp().strip()
|
82
|
-
if param.eql?('AWS_ACCESS_KEY_ID')
|
83
|
-
@access_key_id = value
|
84
|
-
elsif param.eql?('AWS_SECRET_ACCESS_KEY')
|
85
|
-
@secret_access_key = value
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
}
|
91
|
-
elsif @credentials.length == 2
|
92
|
-
@access_key_id = @credentials[0]
|
93
|
-
@secret_access_key = @credentials[1]
|
94
|
-
else
|
95
|
-
raise ArgumentError.new('Credentials must be of the form "/path/to/file" or ["id", "secret"]')
|
96
|
-
end
|
97
|
-
|
98
|
-
if @access_key_id.nil? or @secret_access_key.nil?
|
99
|
-
raise ArgumentError.new('Missing AWS credentials')
|
100
|
-
end
|
101
|
-
|
102
|
-
if @bucket.nil?
|
103
|
-
raise ArgumentError.new('Missing AWS bucket')
|
104
|
-
end
|
73
|
+
@region = get_region
|
105
74
|
|
106
|
-
|
107
|
-
if ENV['HOME'].nil?
|
108
|
-
raise ArgumentError.new('No HOME or sincedb_path set')
|
109
|
-
end
|
110
|
-
@sincedb_path = File.join(ENV["HOME"], ".sincedb_" + Digest::MD5.hexdigest("#{@bucket}+#{@prefix}"))
|
111
|
-
end
|
75
|
+
@logger.info("Registering s3 input", :bucket => @bucket, :region => @region)
|
112
76
|
|
113
|
-
s3 =
|
114
|
-
:access_key_id => @access_key_id,
|
115
|
-
:secret_access_key => @secret_access_key,
|
116
|
-
:region => @region_endpoint
|
117
|
-
)
|
77
|
+
s3 = get_s3object
|
118
78
|
|
119
79
|
@s3bucket = s3.buckets[@bucket]
|
120
80
|
|
@@ -128,152 +88,212 @@ class LogStash::Inputs::S3 < LogStash::Inputs::Base
|
|
128
88
|
unless @backup_to_dir.nil?
|
129
89
|
Dir.mkdir(@backup_to_dir, 0700) unless File.exists?(@backup_to_dir)
|
130
90
|
end
|
131
|
-
|
132
91
|
end # def register
|
133
92
|
|
93
|
+
|
134
94
|
public
|
135
95
|
def run(queue)
|
136
|
-
|
137
|
-
|
138
|
-
sleep(@interval)
|
96
|
+
Stud.interval(@interval) do
|
97
|
+
process_files(queue)
|
139
98
|
end
|
140
|
-
finished
|
141
99
|
end # def run
|
142
100
|
|
143
|
-
|
144
|
-
def
|
101
|
+
public
|
102
|
+
def list_new_files
|
103
|
+
objects = {}
|
145
104
|
|
146
|
-
|
147
|
-
|
148
|
-
end
|
105
|
+
@s3bucket.objects.with_prefix(@prefix).each do |log|
|
106
|
+
@logger.debug("S3 input: Found key", :key => log.key)
|
149
107
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
108
|
+
unless ignore_filename?(log.key)
|
109
|
+
if sincedb.newer?(log.last_modified)
|
110
|
+
objects[log.key] = log.last_modified
|
111
|
+
@logger.debug("S3 input: Adding to objects[]", :key => log.key)
|
112
|
+
end
|
113
|
+
end
|
156
114
|
end
|
115
|
+
return objects.keys.sort {|a,b| objects[a] <=> objects[b]}
|
116
|
+
end # def fetch_new_files
|
157
117
|
|
158
|
-
end # def process_new
|
159
118
|
|
160
|
-
|
161
|
-
def
|
162
|
-
|
163
|
-
|
164
|
-
|
119
|
+
public
|
120
|
+
def backup_to_bucket(object, key)
|
121
|
+
unless @backup_to_bucket.nil?
|
122
|
+
backup_key = "#{@backup_add_prefix}#{key}"
|
123
|
+
if @delete
|
124
|
+
object.move_to(backup_key, :bucket => @backup_bucket)
|
125
|
+
else
|
126
|
+
object.copy_to(backup_key, :bucket => @backup_bucket)
|
127
|
+
end
|
165
128
|
end
|
129
|
+
end
|
166
130
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
end
|
131
|
+
public
|
132
|
+
def backup_to_dir(filename)
|
133
|
+
unless @backup_to_dir.nil?
|
134
|
+
FileUtils.cp(filename, @backup_to_dir)
|
172
135
|
end
|
136
|
+
end
|
173
137
|
|
174
|
-
|
138
|
+
private
|
139
|
+
def process_local_log(queue, filename)
|
140
|
+
@codec.decode(File.open(filename, 'rb')) do |event|
|
141
|
+
decorate(event)
|
142
|
+
queue << event
|
143
|
+
end
|
144
|
+
end # def process_local_log
|
145
|
+
|
146
|
+
private
|
147
|
+
def sincedb
|
148
|
+
@sincedb ||= if @sincedb_path.nil?
|
149
|
+
@logger.info("Using default generated file for the sincedb", :filename => sincedb_file)
|
150
|
+
SinceDB::File.new(sincedb_file)
|
151
|
+
else
|
152
|
+
@logger.error("S3 input: Configuration error, no HOME or sincedb_path set")
|
153
|
+
SinceDB::File.new(@sincedb_path)
|
154
|
+
end
|
155
|
+
end
|
175
156
|
|
176
|
-
|
157
|
+
private
|
158
|
+
def sincedb_file
|
159
|
+
File.join(ENV["HOME"], ".sincedb_" + Digest::MD5.hexdigest("#{@bucket}+#{@prefix}"))
|
160
|
+
end
|
177
161
|
|
178
162
|
private
|
179
|
-
def
|
163
|
+
def process_files(queue, since=nil)
|
164
|
+
objects = list_new_files
|
165
|
+
objects.each do |key|
|
166
|
+
@logger.debug("S3 input processing", :bucket => @bucket, :key => key)
|
180
167
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
object.read do |chunk|
|
187
|
-
s3file.write(chunk)
|
188
|
-
end
|
189
|
-
end
|
190
|
-
process_local_log(queue, filename)
|
191
|
-
unless @backup_to_bucket.nil?
|
192
|
-
backup_object = @backup_bucket.objects[key]
|
193
|
-
backup_object.write(Pathname.new(filename))
|
194
|
-
end
|
195
|
-
unless @backup_to_dir.nil?
|
196
|
-
FileUtils.cp(filename, @backup_to_dir)
|
197
|
-
end
|
198
|
-
if @delete
|
199
|
-
object.delete()
|
200
|
-
end
|
168
|
+
lastmod = @s3bucket.objects[key].last_modified
|
169
|
+
|
170
|
+
process_log(queue, key)
|
171
|
+
|
172
|
+
sincedb.write(lastmod)
|
201
173
|
end
|
202
|
-
|
174
|
+
end # def process_files
|
203
175
|
|
204
|
-
|
176
|
+
private
|
177
|
+
def ignore_filename?(filename)
|
178
|
+
if (@backup_add_prefix && @backup_to_bucket == @bucket && filename =~ /^#{backup_add_prefix}/)
|
179
|
+
return true
|
180
|
+
elsif @exclude_pattern.nil?
|
181
|
+
return false
|
182
|
+
elsif filename =~ Regexp.new(@exclude_pattern)
|
183
|
+
return true
|
184
|
+
else
|
185
|
+
return false
|
186
|
+
end
|
187
|
+
end
|
205
188
|
|
206
189
|
private
|
207
|
-
def
|
190
|
+
def process_log(queue, key)
|
191
|
+
object = @s3bucket.objects[key]
|
208
192
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
193
|
+
tmp = Stud::Temporary.directory("logstash-")
|
194
|
+
|
195
|
+
filename = File.join(tmp, File.basename(key))
|
196
|
+
|
197
|
+
download_remote_file(object, filename)
|
198
|
+
|
199
|
+
process_local_log(queue, filename)
|
200
|
+
|
201
|
+
backup_to_bucket(object, key)
|
202
|
+
backup_to_dir(filename)
|
203
|
+
|
204
|
+
delete_file_from_bucket(object)
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
def download_remote_file(remote_object, local_filename)
|
209
|
+
@logger.debug("S3 input: Download remove file", :remote_key => remote_object.key, :local_filename => local_filename)
|
210
|
+
File.open(local_filename, 'wb') do |s3file|
|
211
|
+
remote_object.read do |chunk|
|
212
|
+
s3file.write(chunk)
|
223
213
|
end
|
224
214
|
end
|
225
|
-
|
226
|
-
end # def process_local_log
|
215
|
+
end
|
227
216
|
|
228
217
|
private
|
229
|
-
def
|
218
|
+
def delete_file_from_bucket(object)
|
219
|
+
if @delete and @backup_to_bucket.nil?
|
220
|
+
object.delete()
|
221
|
+
end
|
222
|
+
end
|
230
223
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
elsif /#Fields: .+/.match(line)
|
237
|
-
junk, format = line.strip().split(/#Fields: (.+)/)
|
238
|
-
unless format.nil?
|
239
|
-
metadata[:format] = format
|
240
|
-
end
|
224
|
+
private
|
225
|
+
def get_region
|
226
|
+
# TODO: (ph) Deprecated, it will be removed
|
227
|
+
if @region_endpoint
|
228
|
+
@region_endpoint
|
241
229
|
else
|
242
|
-
@
|
243
|
-
decorate(event)
|
244
|
-
unless metadata[:version].nil?
|
245
|
-
event["cloudfront_version"] = metadata[:version]
|
246
|
-
end
|
247
|
-
unless metadata[:format].nil?
|
248
|
-
event["cloudfront_fields"] = metadata[:format]
|
249
|
-
end
|
250
|
-
queue << event
|
251
|
-
end
|
230
|
+
@region
|
252
231
|
end
|
253
|
-
|
254
|
-
|
255
|
-
end # def process_line
|
232
|
+
end
|
256
233
|
|
257
234
|
private
|
258
|
-
def
|
235
|
+
def get_s3object
|
236
|
+
# TODO: (ph) Deprecated, it will be removed
|
237
|
+
if @credentials.length == 1
|
238
|
+
File.open(@credentials[0]) { |f| f.each do |line|
|
239
|
+
unless (/^\#/.match(line))
|
240
|
+
if(/\s*=\s*/.match(line))
|
241
|
+
param, value = line.split('=', 2)
|
242
|
+
param = param.chomp().strip()
|
243
|
+
value = value.chomp().strip()
|
244
|
+
if param.eql?('AWS_ACCESS_KEY_ID')
|
245
|
+
@access_key_id = value
|
246
|
+
elsif param.eql?('AWS_SECRET_ACCESS_KEY')
|
247
|
+
@secret_access_key = value
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
}
|
253
|
+
elsif @credentials.length == 2
|
254
|
+
@access_key_id = @credentials[0]
|
255
|
+
@secret_access_key = @credentials[1]
|
256
|
+
end
|
259
257
|
|
260
|
-
if
|
261
|
-
|
258
|
+
if @credentials
|
259
|
+
s3 = AWS::S3.new(
|
260
|
+
:access_key_id => @access_key_id,
|
261
|
+
:secret_access_key => @secret_access_key,
|
262
|
+
:region => @region
|
263
|
+
)
|
262
264
|
else
|
263
|
-
|
265
|
+
s3 = AWS::S3.new(aws_options_hash)
|
264
266
|
end
|
265
|
-
|
266
|
-
|
267
|
-
end # def sincedb_read
|
267
|
+
end
|
268
268
|
|
269
269
|
private
|
270
|
-
def
|
270
|
+
def aws_service_endpoint(region)
|
271
|
+
return { :s3_endpoint => region }
|
272
|
+
end
|
273
|
+
|
274
|
+
module SinceDB
|
275
|
+
class File
|
276
|
+
def initialize(file)
|
277
|
+
@sincedb_path = file
|
278
|
+
end
|
271
279
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
File.open(@sincedb_path, 'w') { |file| file.write(since.to_s) }
|
280
|
+
def newer?(date)
|
281
|
+
date > read
|
282
|
+
end
|
276
283
|
|
277
|
-
|
284
|
+
def read
|
285
|
+
if ::File.exists?(@sincedb_path)
|
286
|
+
since = Time.parse(::File.read(@sincedb_path).chomp.strip)
|
287
|
+
else
|
288
|
+
since = Time.new(0)
|
289
|
+
end
|
290
|
+
return since
|
291
|
+
end
|
278
292
|
|
293
|
+
def write(since = nil)
|
294
|
+
since = Time.now() if since.nil?
|
295
|
+
::File.open(@sincedb_path, 'w') { |file| file.write(since.to_s) }
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
279
299
|
end # class LogStash::Inputs::S3
|
data/logstash-input-s3.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
|
3
3
|
s.name = 'logstash-input-s3'
|
4
|
-
s.version = '0.1.
|
4
|
+
s.version = '0.1.2'
|
5
5
|
s.licenses = ['Apache License (2.0)']
|
6
6
|
s.summary = "Stream events from files from a S3 bucket."
|
7
7
|
s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
|
@@ -22,8 +22,9 @@ Gem::Specification.new do |s|
|
|
22
22
|
# Gem dependencies
|
23
23
|
s.add_runtime_dependency 'logstash', '>= 1.4.0', '< 2.0.0'
|
24
24
|
s.add_runtime_dependency 'logstash-mixin-aws'
|
25
|
-
s.add_runtime_dependency 'logstash-codec-
|
25
|
+
s.add_runtime_dependency 'logstash-codec-line'
|
26
26
|
s.add_runtime_dependency 'aws-sdk'
|
27
|
+
s.add_runtime_dependency 'stud', '~> 0.0.18'
|
27
28
|
|
28
29
|
s.add_development_dependency 'logstash-devutils'
|
29
30
|
end
|
data/spec/inputs/s3_spec.rb
CHANGED
@@ -1,5 +1,146 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require "logstash/devutils/rspec/spec_helper"
|
3
|
+
require "logstash/inputs/s3"
|
4
|
+
require "logstash/errors"
|
2
5
|
require 'logstash/inputs/s3'
|
3
6
|
|
7
|
+
require "aws-sdk"
|
8
|
+
require "stud/temporary"
|
9
|
+
|
4
10
|
describe LogStash::Inputs::S3 do
|
11
|
+
before do
|
12
|
+
AWS.stub!
|
13
|
+
Thread.abort_on_exception = true
|
14
|
+
end
|
15
|
+
let(:day) { 3600 * 24 }
|
16
|
+
let(:settings) {
|
17
|
+
{
|
18
|
+
"access_key_id" => "1234",
|
19
|
+
"secret_access_key" => "secret",
|
20
|
+
"bucket" => "logstash-test"
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
describe "#list_new_files" do
|
25
|
+
before { allow_any_instance_of(AWS::S3::ObjectCollection).to receive(:with_prefix).with(nil) { objects_list } }
|
26
|
+
|
27
|
+
let(:present_object) { double(:key => 'this-should-be-present', :last_modified => Time.now) }
|
28
|
+
let(:objects_list) {
|
29
|
+
[
|
30
|
+
double(:key => 'exclude-this-file-1', :last_modified => Time.now - 2 * day),
|
31
|
+
double(:key => 'exclude/logstash', :last_modified => Time.now - 2 * day),
|
32
|
+
present_object
|
33
|
+
]
|
34
|
+
}
|
35
|
+
|
36
|
+
it 'should allow user to exclude files from the s3 bucket' do
|
37
|
+
config = LogStash::Inputs::S3.new(settings.merge({ "exclude_pattern" => "^exclude" }))
|
38
|
+
config.register
|
39
|
+
expect(config.list_new_files).to eq([present_object.key])
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should support not providing a exclude pattern' do
|
43
|
+
config = LogStash::Inputs::S3.new(settings)
|
44
|
+
config.register
|
45
|
+
expect(config.list_new_files).to eq(objects_list.map(&:key))
|
46
|
+
end
|
47
|
+
|
48
|
+
context "If the bucket is the same as the backup bucket" do
|
49
|
+
it 'should ignore files from the bucket if they match the backup prefix' do
|
50
|
+
objects_list = [
|
51
|
+
double(:key => 'mybackup-log-1', :last_modified => Time.now),
|
52
|
+
present_object
|
53
|
+
]
|
54
|
+
|
55
|
+
allow_any_instance_of(AWS::S3::ObjectCollection).to receive(:with_prefix).with(nil) { objects_list }
|
56
|
+
|
57
|
+
config = LogStash::Inputs::S3.new(settings.merge({ 'backup_add_prefix' => 'mybackup',
|
58
|
+
'backup_to_bucket' => settings['bucket']}))
|
59
|
+
config.register
|
60
|
+
expect(config.list_new_files).to eq([present_object.key])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should ignore files older than X' do
|
65
|
+
config = LogStash::Inputs::S3.new(settings.merge({ 'backup_add_prefix' => 'exclude-this-file'}))
|
66
|
+
|
67
|
+
expect_any_instance_of(LogStash::Inputs::S3::SinceDB::File).to receive(:read).exactly(objects_list.size) { Time.now - day }
|
68
|
+
config.register
|
69
|
+
|
70
|
+
expect(config.list_new_files).to eq([present_object.key])
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should sort return object sorted by last_modification date with older first' do
|
74
|
+
objects = [
|
75
|
+
double(:key => 'YESTERDAY', :last_modified => Time.now - day),
|
76
|
+
double(:key => 'TODAY', :last_modified => Time.now),
|
77
|
+
double(:key => 'TWO_DAYS_AGO', :last_modified => Time.now - 2 * day)
|
78
|
+
]
|
79
|
+
|
80
|
+
allow_any_instance_of(AWS::S3::ObjectCollection).to receive(:with_prefix).with(nil) { objects }
|
81
|
+
|
82
|
+
|
83
|
+
config = LogStash::Inputs::S3.new(settings)
|
84
|
+
config.register
|
85
|
+
expect(config.list_new_files).to eq(['TWO_DAYS_AGO', 'YESTERDAY', 'TODAY'])
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "when doing backup on the s3" do
|
89
|
+
it 'should copy to another s3 bucket when keeping the original file' do
|
90
|
+
config = LogStash::Inputs::S3.new(settings.merge({ "backup_to_bucket" => "mybackup"}))
|
91
|
+
config.register
|
92
|
+
|
93
|
+
s3object = double()
|
94
|
+
expect(s3object).to receive(:copy_to).with('test-file', :bucket => an_instance_of(AWS::S3::Bucket))
|
95
|
+
|
96
|
+
config.backup_to_bucket(s3object, 'test-file')
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should move to another s3 bucket when deleting the original file' do
|
100
|
+
config = LogStash::Inputs::S3.new(settings.merge({ "backup_to_bucket" => "mybackup", "delete" => true }))
|
101
|
+
config.register
|
102
|
+
|
103
|
+
s3object = double()
|
104
|
+
expect(s3object).to receive(:move_to).with('test-file', :bucket => an_instance_of(AWS::S3::Bucket))
|
105
|
+
|
106
|
+
config.backup_to_bucket(s3object, 'test-file')
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should add the specified prefix to the backup file' do
|
110
|
+
config = LogStash::Inputs::S3.new(settings.merge({ "backup_to_bucket" => "mybackup",
|
111
|
+
"backup_add_prefix" => 'backup-' }))
|
112
|
+
config.register
|
113
|
+
|
114
|
+
s3object = double()
|
115
|
+
expect(s3object).to receive(:copy_to).with('backup-test-file', :bucket => an_instance_of(AWS::S3::Bucket))
|
116
|
+
|
117
|
+
config.backup_to_bucket(s3object, 'test-file')
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should support doing local backup of files' do
|
122
|
+
Stud::Temporary.directory do |backup_dir|
|
123
|
+
Stud::Temporary.file do |source_file|
|
124
|
+
backup_file = File.join(backup_dir.to_s, Pathname.new(source_file.path).basename.to_s)
|
125
|
+
|
126
|
+
config = LogStash::Inputs::S3.new(settings.merge({ "backup_to_dir" => backup_dir }))
|
127
|
+
|
128
|
+
config.backup_to_dir(source_file)
|
129
|
+
|
130
|
+
expect(File.exists?(backup_file)).to be_true
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'should accepts a list of credentials for the aws-sdk, this is deprecated' do
|
136
|
+
old_credentials_settings = {
|
137
|
+
"credentials" => ['1234', 'secret'],
|
138
|
+
"backup_to_dir" => "/tmp/mybackup",
|
139
|
+
"bucket" => "logstash-test"
|
140
|
+
}
|
141
|
+
|
142
|
+
config = LogStash::Inputs::S3.new(old_credentials_settings)
|
143
|
+
expect{ config.register }.not_to raise_error
|
144
|
+
end
|
145
|
+
end
|
5
146
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-input-s3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Elasticsearch
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-01-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: logstash
|
@@ -45,7 +45,7 @@ dependencies:
|
|
45
45
|
prerelease: false
|
46
46
|
type: :runtime
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
|
-
name: logstash-codec-
|
48
|
+
name: logstash-codec-line
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
51
|
- - '>='
|
@@ -72,6 +72,20 @@ dependencies:
|
|
72
72
|
version: '0'
|
73
73
|
prerelease: false
|
74
74
|
type: :runtime
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: stud
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ~>
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 0.0.18
|
82
|
+
requirement: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ~>
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: 0.0.18
|
87
|
+
prerelease: false
|
88
|
+
type: :runtime
|
75
89
|
- !ruby/object:Gem::Dependency
|
76
90
|
name: logstash-devutils
|
77
91
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -121,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
121
135
|
version: '0'
|
122
136
|
requirements: []
|
123
137
|
rubyforge_project:
|
124
|
-
rubygems_version: 2.
|
138
|
+
rubygems_version: 2.1.9
|
125
139
|
signing_key:
|
126
140
|
specification_version: 4
|
127
141
|
summary: Stream events from files from a S3 bucket.
|