Commit eb45a619 by Isaac Betesh Committed by Jon Yurek

prelimiary steps to supporting aws-sdk-v2: Upgraded Paperclip::Storage::S3 class and the live spec

parent 3cb13318
......@@ -41,7 +41,7 @@ module Paperclip
# * +s3_permissions+: This is a String that should be one of the "canned" access
# policies that S3 provides (more information can be found here:
# http://docs.aws.amazon.com/AmazonS3/latest/dev/ACLOverview.html)
# The default for Paperclip is :public_read.
# The default for Paperclip is :public_read (aws-sdk v1) / public-read (aws-sdk v2).
#
# You can set permission on a per style bases by doing the following:
# :s3_permissions => {
......@@ -93,6 +93,7 @@ module Paperclip
# S3 (strictly speaking) does not support directories, you can still use a / to
# separate parts of your file name.
# * +s3_host_name+: If you are using your bucket in Tokyo region etc, write host_name.
# * +s3_region+: For aws-sdk v2, s3_region is required.
# * +s3_metadata+: These key/value pairs will be stored with the
# object. This option works by prefixing each key with
# "x-amz-meta-" before sending it as a header on the object
......@@ -114,20 +115,22 @@ module Paperclip
def self.extended base
begin
require 'aws-sdk'
const_set('AWS_CLASS', defined?(::AWS) ? ::AWS : ::Aws)
const_set('DEFAULT_PERMISSION', defined?(::AWS) ? :public_read : :'public-read')
rescue LoadError => e
e.message << " (You may need to install the aws-sdk gem)"
raise e
end unless defined?(AWS::Core)
end unless defined?(AWS_CLASS)
# Overriding log formatter to make sure it return a UTF-8 string
if defined?(AWS::Core::LogFormatter)
AWS::Core::LogFormatter.class_eval do
if defined?(AWS_CLASS::Core::LogFormatter)
AWS_CLASS::Core::LogFormatter.class_eval do
def summarize_hash(hash)
hash.map { |key, value| ":#{key}=>#{summarize_value(value)}".force_encoding('UTF-8') }.sort.join(',')
end
end
elsif defined?(AWS::Core::ClientLogging)
AWS::Core::ClientLogging.class_eval do
elsif defined?(AWS_CLASS::Core::ClientLogging)
AWS_CLASS::Core::ClientLogging.class_eval do
def sanitize_hash(hash)
hash.map { |key, value| "#{sanitize_value(key)}=>#{sanitize_value(value)}".force_encoding('UTF-8') }.sort.join(',')
end
......@@ -141,7 +144,7 @@ module Paperclip
Proc.new do |style, attachment|
permission = (@s3_permissions[style.to_s.to_sym] || @s3_permissions[:default])
permission = permission.call(attachment, style) if permission.respond_to?(:call)
(permission == :public_read) ? 'http' : 'https'
(permission == DEFAULT_PERMISSION) ? 'http' : 'https'
end
@s3_metadata = @options[:s3_metadata] || {}
@s3_headers = {}
......@@ -149,7 +152,7 @@ module Paperclip
@s3_storage_class = set_storage_class(@options[:s3_storage_class])
@s3_server_side_encryption = :aes256
@s3_server_side_encryption = aws_v1? ? :aes256 : "AES256"
if @options[:s3_server_side_encryption].blank?
@s3_server_side_encryption = false
end
......@@ -200,6 +203,13 @@ module Paperclip
host_name || s3_credentials[:s3_host_name] || "s3.amazonaws.com"
end
def s3_region
region = @options[:s3_region]
region = region.call(self) if region.is_a?(Proc)
region || s3_credentials[:s3_region]
end
def s3_host_alias
@s3_host_alias = @options[:s3_host_alias]
@s3_host_alias = @s3_host_alias.call(self) if @s3_host_alias.respond_to?(:call)
......@@ -220,7 +230,11 @@ module Paperclip
def s3_interface
@s3_interface ||= begin
config = { :s3_endpoint => s3_host_name }
config = if aws_v1?
{ :s3_endpoint => s3_host_name }
else
{ :region => s3_region }
end
if using_http_proxy?
......@@ -244,15 +258,31 @@ module Paperclip
def obtain_s3_instance_for(options)
instances = (Thread.current[:paperclip_s3_instances] ||= {})
instances[options] ||= AWS::S3.new(options)
instances[options] ||= if aws_v1?
AWS_CLASS::S3.new(options)
else
AWS_CLASS::S3::Resource.new(options)
end
end
def s3_bucket
@s3_bucket ||= s3_interface.buckets[bucket_name]
@s3_bucket ||= if aws_v1?
s3_interface.buckets[bucket_name]
else
s3_interface.bucket(bucket_name)
end
end
def style_name_as_path(style_name)
path(style_name).sub(%r{\A/},'')
end
def s3_object style_name = default_style
s3_bucket.objects[path(style_name).sub(%r{\A/},'')]
if aws_v1?
s3_bucket.objects[style_name_as_path(style_name)]
else
s3_bucket.object(style_name_as_path(style_name))
end
end
def using_http_proxy?
......@@ -277,7 +307,7 @@ module Paperclip
def set_permissions permissions
permissions = { :default => permissions } unless permissions.respond_to?(:merge)
permissions.merge :default => (permissions[:default] || :public_read)
permissions.merge :default => (permissions[:default] || DEFAULT_PERMISSION)
end
def set_storage_class(storage_class)
......@@ -297,7 +327,7 @@ module Paperclip
else
false
end
rescue AWS::Errors::Base => e
rescue AWS_CLASS::Errors::Base => e
false
end
......@@ -356,11 +386,11 @@ module Paperclip
write_options[:metadata] = @s3_metadata unless @s3_metadata.empty?
write_options.merge!(@s3_headers)
s3_object(style).write(file, write_options)
rescue AWS::S3::Errors::NoSuchBucket
s3_object(style).send((aws_v1? ? :write : :put), file, write_options)
rescue AWS_CLASS::S3::Errors::NoSuchBucket
create_bucket
retry
rescue AWS::S3::Errors::SlowDown
rescue AWS_CLASS::S3::Errors::SlowDown
retries += 1
if retries <= 5
sleep((2 ** retries) * 0.5)
......@@ -382,8 +412,12 @@ module Paperclip
@queued_for_delete.each do |path|
begin
log("deleting #{path}")
s3_bucket.objects[path.sub(%r{\A/},'')].delete
rescue AWS::Errors::Base => e
if aws_v1?
s3_bucket.objects[path.sub(%r{\A/},'')]
else
s3_bucket.object(path.sub(%r{\A/},''))
end.delete
rescue AWS_CLASS::Errors::Base => e
# Ignore this.
end
end
......@@ -393,17 +427,21 @@ module Paperclip
def copy_to_local_file(style, local_dest_path)
log("copying #{path(style)} to local file #{local_dest_path}")
::File.open(local_dest_path, 'wb') do |local_file|
s3_object(style).read do |chunk|
s3_object(style).send(aws_v1? ? :read : :get) do |chunk|
local_file.write(chunk)
end
end
rescue AWS::Errors::Base => e
rescue AWS_CLASS::Errors::Base => e
warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}")
false
end
private
def aws_v1?
Gem::Version.new(AWS_CLASS::VERSION) < Gem::Version.new(2)
end
def find_credentials creds
case creds
when File
......
......@@ -31,7 +31,7 @@ Gem::Specification.new do |s|
s.add_development_dependency('rspec', '~> 3.0')
s.add_development_dependency('appraisal')
s.add_development_dependency('mocha')
s.add_development_dependency('aws-sdk', '>= 1.5.7', "<= 2.0")
s.add_development_dependency('aws-sdk', '>= 1.5.7', "< 3.0")
s.add_development_dependency('bourne')
s.add_development_dependency('cucumber', '~> 1.3.18')
s.add_development_dependency('aruba')
......
......@@ -8,6 +8,7 @@ unless ENV["S3_BUCKET"].blank?
storage: :s3,
bucket: ENV["S3_BUCKET"],
path: ":class/:attachment/:id/:style.:extension",
s3_region: ENV["S3_REGION"],
s3_credentials: {
aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
aws_secre_access_key: ENV['AWS_SECRET_ACCESS_KEY']
......@@ -45,6 +46,7 @@ unless ENV["S3_BUCKET"].blank?
storage: :s3,
bucket: ENV["S3_BUCKET"],
path: ":class/:attachment/:id/:style.:extension",
s3_region: ENV["S3_REGION"],
s3_credentials: {
aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
aws_secre_access_key: ENV['AWS_SECRET_ACCESS_KEY']
......@@ -64,6 +66,7 @@ unless ENV["S3_BUCKET"].blank?
storage: :s3,
bucket: ENV["S3_BUCKET"],
path: ":class/:attachment/:id/:style.:extension",
s3_region: ENV["S3_REGION"],
s3_credentials: {
aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
aws_secre_access_key: ENV['AWS_SECRET_ACCESS_KEY']
......@@ -105,6 +108,7 @@ unless ENV["S3_BUCKET"].blank?
rebuild_model styles: { thumb: "100x100", square: "32x32#" },
storage: :s3,
bucket: ENV["S3_BUCKET"],
s3_region: ENV["S3_REGION"],
s3_credentials: {
aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
aws_secre_access_key: ENV['AWS_SECRET_ACCESS_KEY']
......@@ -141,17 +145,18 @@ unless ENV["S3_BUCKET"].blank?
end
context "An attachment that uses S3 for storage and uses AES256 encryption" do
let(:encryption_method) { defined?(::AWS) ? :aes356 : "AES256" }
before do
rebuild_model styles: { thumb: "100x100", square: "32x32#" },
storage: :s3,
bucket: ENV["S3_BUCKET"],
path: ":class/:attachment/:id/:style.:extension",
s3_region: ENV["S3_REGION"],
s3_credentials: {
aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
aws_secre_access_key: ENV['AWS_SECRET_ACCESS_KEY']
},
s3_server_side_encryption: :aes256
s3_server_side_encryption: encryption_method
Dummy.delete_all
@dummy = Dummy.new
end
......@@ -173,7 +178,7 @@ unless ENV["S3_BUCKET"].blank?
end
it "is encrypted on S3" do
assert @dummy.avatar.s3_object.server_side_encryption == :aes256
assert @dummy.avatar.s3_object.server_side_encryption == encryption_method
end
end
end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment