logstash-output-s3 4.0.3 → 4.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/lib/logstash/outputs/s3.rb +19 -3
- data/lib/logstash/outputs/s3/temporary_file.rb +2 -2
- data/lib/logstash/outputs/s3/uploader.rb +4 -1
- data/lib/logstash/outputs/s3/write_bucket_permission_validator.rb +9 -5
- data/logstash-output-s3.gemspec +1 -1
- data/spec/integration/time_based_rotation_with_stale_write_spec.rb +5 -1
- data/spec/outputs/s3/temporary_file_spec.rb +7 -0
- data/spec/outputs/s3/write_bucket_permission_validator_spec.rb +2 -1
- data/spec/outputs/s3_spec.rb +63 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31a45d07d1d55ba2247ea058ca57be461f7773d9
|
4
|
+
data.tar.gz: c45f5e22b868918990ef27f5c2d2b1682f7ad93e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d05449f29cd48dbbf75372bae6a2b4a6722b23b34e3653f435f1edf49ee56caa473dc52972a4812888b600f8055c98b08ec4002f887cd38646941ffe05814887
|
7
|
+
data.tar.gz: 63ec9d6444fc8046aeaf05d81a7ea2a2e6e05b9d986538713d7e3357aa430ee1d3f6f49a7ae0ab15f1dd4950767aa8e13e8e1b8be72205167b132596f8348492
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## 4.0.5
|
2
|
+
- Delete the file on disk after they are succesfully uploaded to S3 #122 #120
|
3
|
+
- Added logging when an exception occur in the Uploader's `on_complete` callback
|
4
|
+
|
5
|
+
## 4.0.4
|
6
|
+
- Add support for `storage_class` configuration
|
7
|
+
- Fix compatibility with Logstash 2.4
|
8
|
+
- Add support for `aws:kms` server side encryption #104
|
9
|
+
|
1
10
|
## 4.0.3
|
2
11
|
- When configuring the `canned_acl` options of the plugins the role was not applied correctly to the created object: #7
|
3
12
|
|
data/lib/logstash/outputs/s3.rb
CHANGED
@@ -127,9 +127,23 @@ class LogStash::Outputs::S3 < LogStash::Outputs::Base
|
|
127
127
|
config :canned_acl, :validate => ["private", "public_read", "public_read_write", "authenticated_read"],
|
128
128
|
:default => "private"
|
129
129
|
|
130
|
-
# Specifies wether or not to use S3's
|
130
|
+
# Specifies wether or not to use S3's server side encryption. Defaults to no encryption.
|
131
131
|
config :server_side_encryption, :validate => :boolean, :default => false
|
132
132
|
|
133
|
+
# Specifies what type of encryption to use when SSE is enabled.
|
134
|
+
config :server_side_encryption_algorithm, :validate => ["AES256", "aws:kms"], :default => "AES256"
|
135
|
+
|
136
|
+
# The key to use when specified along with server_side_encryption => aws:kms.
|
137
|
+
# If server_side_encryption => aws:kms is set but this is not default KMS key is used.
|
138
|
+
# http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingKMSEncryption.html
|
139
|
+
config :ssekms_key_id, :validate => :string
|
140
|
+
|
141
|
+
# Specifies what S3 storage class to use when uploading the file.
|
142
|
+
# More information about the different storage classes can be found:
|
143
|
+
# http://docs.aws.amazon.com/AmazonS3/latest/dev/storage-class-intro.html
|
144
|
+
# Defaults to STANDARD.
|
145
|
+
config :storage_class, :validate => ["STANDARD", "REDUCED_REDUNDANCY", "STANDARD_IA"], :default => "STANDARD"
|
146
|
+
|
133
147
|
# Set the directory where logstash will store the tmp files before sending it to S3
|
134
148
|
# default to the current OS temporary directory in linux /tmp/logstash
|
135
149
|
config :temporary_directory, :validate => :string, :default => File.join(Dir.tmpdir, "logstash")
|
@@ -183,7 +197,7 @@ class LogStash::Outputs::S3 < LogStash::Outputs::Base
|
|
183
197
|
raise LogStash::ConfigurationError, "Logstash must have the permissions to write to the temporary directory: #{@temporary_directory}"
|
184
198
|
end
|
185
199
|
|
186
|
-
if @validate_credentials_on_root_bucket && !WriteBucketPermissionValidator.valid?(bucket_resource)
|
200
|
+
if @validate_credentials_on_root_bucket && !WriteBucketPermissionValidator.new(@logger).valid?(bucket_resource)
|
187
201
|
raise LogStash::ConfigurationError, "Logstash must have the privileges to write to root bucket `#{@bucket}`, check you credentials or your permissions."
|
188
202
|
end
|
189
203
|
|
@@ -265,7 +279,9 @@ class LogStash::Outputs::S3 < LogStash::Outputs::Base
|
|
265
279
|
def upload_options
|
266
280
|
{
|
267
281
|
:acl => @canned_acl,
|
268
|
-
:server_side_encryption => @server_side_encryption ?
|
282
|
+
:server_side_encryption => @server_side_encryption ? @server_side_encryption_algorithm : nil,
|
283
|
+
:ssekms_key_id => @server_side_encryption_algorithm == "aws:kms" ? @ssekms_key_id : nil,
|
284
|
+
:storage_class => @storage_class,
|
269
285
|
:content_encoding => @encoding == "gzip" ? "gzip" : nil
|
270
286
|
}
|
271
287
|
end
|
@@ -50,8 +50,8 @@ module LogStash
|
|
50
50
|
# we delete the root of the UUID, using a UUID also remove the risk of deleting unwanted file, it acts as
|
51
51
|
# a sandbox.
|
52
52
|
def delete!
|
53
|
-
@fd.close
|
54
|
-
|
53
|
+
@fd.close rescue IOError # force close anyway
|
54
|
+
FileUtils.rm_r(@temp_path, :secure => true)
|
55
55
|
end
|
56
56
|
|
57
57
|
def empty?
|
@@ -42,11 +42,14 @@ module LogStash
|
|
42
42
|
#
|
43
43
|
# Thread might be stuck here, but I think its better than losing anything
|
44
44
|
# its either a transient errors or something bad really happened.
|
45
|
-
logger.error("Uploading failed, retrying", :exception => e, :path => file.path, :backtrace => e.backtrace)
|
45
|
+
logger.error("Uploading failed, retrying", :exception => e.class, :message => e.message, :path => file.path, :backtrace => e.backtrace)
|
46
46
|
retry
|
47
47
|
end
|
48
48
|
|
49
49
|
options[:on_complete].call(file) unless options[:on_complete].nil?
|
50
|
+
rescue => e
|
51
|
+
logger.error("An error occured in the `on_complete` uploader", :exception => e.class, :message => e.message, :path => file.path, :backtrace => e.backtrace)
|
52
|
+
raise e # reraise it since we don't deal with it now
|
50
53
|
end
|
51
54
|
|
52
55
|
def stop
|
@@ -7,14 +7,18 @@ module LogStash
|
|
7
7
|
module Outputs
|
8
8
|
class S3
|
9
9
|
class WriteBucketPermissionValidator
|
10
|
-
|
11
|
-
|
12
|
-
def
|
10
|
+
attr_reader :logger
|
11
|
+
|
12
|
+
def initialize(logger)
|
13
|
+
@logger = logger
|
14
|
+
end
|
15
|
+
|
16
|
+
def valid?(bucket_resource)
|
13
17
|
begin
|
14
18
|
upload_test_file(bucket_resource)
|
15
19
|
true
|
16
20
|
rescue StandardError => e
|
17
|
-
logger.error("Error validating bucket write permissions!",
|
21
|
+
logger.error("Error validating bucket write permissions!",
|
18
22
|
:message => e.message,
|
19
23
|
:class => e.class.name
|
20
24
|
)
|
@@ -23,7 +27,7 @@ module LogStash
|
|
23
27
|
end
|
24
28
|
|
25
29
|
private
|
26
|
-
def
|
30
|
+
def upload_test_file(bucket_resource)
|
27
31
|
generated_at = Time.now
|
28
32
|
|
29
33
|
key = "logstash-programmatic-access-test-object-#{generated_at}"
|
data/logstash-output-s3.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'logstash-output-s3'
|
3
|
-
s.version = '4.0.
|
3
|
+
s.version = '4.0.5'
|
4
4
|
s.licenses = ['Apache-2.0']
|
5
5
|
s.summary = "This plugin was created for store the logstash's events into Amazon Simple Storage Service (Amazon S3)"
|
6
6
|
s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
|
@@ -26,7 +26,7 @@ describe "File Time rotation with stale write", :integration => true do
|
|
26
26
|
clean_remote_files(prefix)
|
27
27
|
subject.register
|
28
28
|
subject.multi_receive_encoded(batch)
|
29
|
-
sleep(
|
29
|
+
sleep(5) # the periodic check should have kick in
|
30
30
|
end
|
31
31
|
|
32
32
|
after do
|
@@ -56,5 +56,9 @@ describe "File Time rotation with stale write", :integration => true do
|
|
56
56
|
try(20) do
|
57
57
|
expect(Dir.glob(File.join(download_directory, "**", "*.txt")).inject(0) { |sum, f| sum + IO.readlines(f).size }).to eq(number_of_events)
|
58
58
|
end
|
59
|
+
|
60
|
+
try(10) do
|
61
|
+
expect(Dir.glob(File.join(temporary_directory, "**", "*.txt")).size).to eq(1) # we should only have 1 file left, since we did a rotation
|
62
|
+
end
|
59
63
|
end
|
60
64
|
end
|
@@ -34,6 +34,13 @@ describe LogStash::Outputs::S3::TemporaryFile do
|
|
34
34
|
expect(File.exist?(subject.path)).to be_falsey
|
35
35
|
end
|
36
36
|
|
37
|
+
it "successfully delete a file already closed" do
|
38
|
+
subject.close
|
39
|
+
expect(File.exist?(subject.path)).to be_truthy
|
40
|
+
subject.delete!
|
41
|
+
expect(File.exist?(subject.path)).to be_falsey
|
42
|
+
end
|
43
|
+
|
37
44
|
it "returns the creation time" do
|
38
45
|
expect(subject.ctime).to be < Time.now + 0.5
|
39
46
|
end
|
@@ -4,12 +4,13 @@ require "logstash/outputs/s3/write_bucket_permission_validator"
|
|
4
4
|
require "aws-sdk"
|
5
5
|
|
6
6
|
describe LogStash::Outputs::S3::WriteBucketPermissionValidator do
|
7
|
+
let(:logger) { spy(:logger ) }
|
7
8
|
let(:bucket_name) { "foobar" }
|
8
9
|
let(:obj) { double("s3_object") }
|
9
10
|
let(:client) { Aws::S3::Client.new(stub_responses: true) }
|
10
11
|
let(:bucket) { Aws::S3::Bucket.new(bucket_name, :client => client) }
|
11
12
|
|
12
|
-
subject { described_class }
|
13
|
+
subject { described_class.new(logger) }
|
13
14
|
|
14
15
|
before do
|
15
16
|
expect(bucket).to receive(:object).with(any_args).and_return(obj)
|
data/spec/outputs/s3_spec.rb
CHANGED
@@ -25,7 +25,7 @@ describe LogStash::Outputs::S3 do
|
|
25
25
|
|
26
26
|
before do
|
27
27
|
allow(subject).to receive(:bucket_resource).and_return(mock_bucket)
|
28
|
-
|
28
|
+
allow_any_instance_of(LogStash::Outputs::S3::WriteBucketPermissionValidator).to receive(:valid?).with(mock_bucket).and_return(true)
|
29
29
|
end
|
30
30
|
|
31
31
|
context "#register configuration validation" do
|
@@ -61,6 +61,67 @@ describe LogStash::Outputs::S3 do
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
+
describe "Service Side Encryption" do
|
65
|
+
|
66
|
+
context "when configured" do
|
67
|
+
it "should be configure" do
|
68
|
+
s3 = described_class.new(options.merge({ "server_side_encryption" => true }))
|
69
|
+
expect(s3.upload_options).to include(:server_side_encryption => "AES256")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "when algorithm is configured" do
|
74
|
+
["AES256", "aws:kms"].each do |sse|
|
75
|
+
it "should return the configured SSE: #{sse}" do
|
76
|
+
s3 = described_class.new(options.merge({ "server_side_encryption" => true, "server_side_encryption_algorithm" => sse }))
|
77
|
+
expect(s3.upload_options).to include(:server_side_encryption => sse)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "when using SSE with KMS and custom key" do
|
83
|
+
it "should return the configured KMS key" do
|
84
|
+
s3 = described_class.new(options.merge({ "server_side_encryption" => true, "server_side_encryption_algorithm" => "aws:kms", "ssekms_key_id" => "test"}))
|
85
|
+
expect(s3.upload_options).to include(:server_side_encryption => "aws:kms")
|
86
|
+
expect(s3.upload_options).to include(:ssekms_key_id => "test")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "when using SSE with KMS but no custom key" do
|
91
|
+
it "should return the configured KMS key" do
|
92
|
+
s3 = described_class.new(options.merge({ "server_side_encryption" => true, "server_side_encryption_algorithm" => "aws:kms"}))
|
93
|
+
expect(s3.upload_options).to include(:server_side_encryption => "aws:kms")
|
94
|
+
expect(s3.upload_options).to include(:ssekms_key_id => nil)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "when not configured" do
|
99
|
+
it "should not be configured" do
|
100
|
+
s3 = described_class.new(options)
|
101
|
+
expect(s3.upload_options).to include(:server_side_encryption => nil)
|
102
|
+
expect(s3.upload_options).to include(:ssekms_key_id => nil)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "Storage Class" do
|
108
|
+
context "when configured" do
|
109
|
+
["STANDARD", "REDUCED_REDUNDANCY", "STANDARD_IA"].each do |storage_class|
|
110
|
+
it "should return the configured storage class: #{storage_class}" do
|
111
|
+
s3 = described_class.new(options.merge({ "storage_class" => storage_class }))
|
112
|
+
expect(s3.upload_options).to include(:storage_class => storage_class)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "when not configured" do
|
118
|
+
it "uses STANDARD as the default" do
|
119
|
+
s3 = described_class.new(options)
|
120
|
+
expect(s3.upload_options).to include(:storage_class => "STANDARD")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
64
125
|
describe "temporary directory" do
|
65
126
|
let(:temporary_directory) { Stud::Temporary.pathname }
|
66
127
|
let(:options) { super.merge({ "temporary_directory" => temporary_directory }) }
|
@@ -84,7 +145,7 @@ describe LogStash::Outputs::S3 do
|
|
84
145
|
|
85
146
|
it "allow to not validate credentials" do
|
86
147
|
s3 = described_class.new(options.merge({"validate_credentials_on_root_bucket" => false}))
|
87
|
-
|
148
|
+
expect_any_instance_of(LogStash::Outputs::S3::WriteBucketPermissionValidator).not_to receive(:valid?).with(any_args)
|
88
149
|
s3.register
|
89
150
|
end
|
90
151
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-output-s3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Elastic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-01-
|
11
|
+
date: 2017-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|