Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Object#copy_from support #7

Merged
merged 2 commits into from
Aug 13, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 82 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,88 @@ Or install it yourself as:

## Usage

TODO: Write usage instructions here
```rb
require 'swift-storage'

# = Configuration =
## With environment variables
#
# SWIFT_STORAGE_AUTH_VERSION
# SWIFT_STORAGE_TENANT
# SWIFT_STORAGE_USERNAME
# SWIFT_STORAGE_PASSWORD
# SWIFT_STORAGE_ENDPOINT
# SWIFT_STORAGE_TEMP_URL_KEY
#
## With Rails initializer
#
# Some parameters are by default configured with the above environment variables, see configuration.rb
SwiftStorage.configure do |config|
config.auth_version = '2.0' # Keystone auth version, default is 1.0
config.tenant = 'Millennium Falcon' # Aka Openstack project
config.username = 'han'
config.password = 'YT-1300'
config.endpoint = 'https//corellia.lan' # Keystone endpoint
config.temp_url_key = '492727ZED' # Secret key for presigned URLs
# ...
end
#
## With service initialization
#
# NB: It overrides initializer configuration
swift = SwiftStorage::Service.new(
tenant: 'Millennium Falcon',
username: 'han',
password: 'YT-1300',
endpoint: 'https//corellia.lan',
temp_url_key: '492727ZED'
)


# Authenticate, primary to retrieve Swift Storage URL
swift.authenticate!
swift.authenticated?
# => true

# Setup Secret key in Swift server
swift.account.write(temp_url_key: '492727ZED')

# Create & get containers
swift.containers['source'].create unless swift.containers['source'].exists?
source = swift.containers['source']
swift.containers['destination'].create unless swift.containers['destination'].exists?
destination = swift.containers['destination']

# Get objects
source_obj = source.objects['Kessel.asteroid']
destination_obj = destination.objects['SiKlaata.cluster']

# Upload data into object
source_obj.write('Glitterstim', content_type: 'application/spice')
# or stream from file
File.open('/tmp/Kessel.asteroid', 'r') do |input|
source_obj.write(input, content_type: 'application/spice')
end

# Copy an object
# Source can be a SwiftStorage::Object or a string like 'source/Kessel.asteroid'
destination_obj.copy_from(source_obj)

# Read data from Swift
p destination_obj.read
# => Glitterstim

# Download to a file
File.open('/tmp/SiKlaata.cluster', 'w') do |output|
destination_obj.read(output)
end
# or
destination_obj.stream_to_file('/tmp/SiKlaata.cluster')

# Create temporary pre-signed URL
p destination_obj.temp_url(Time.now + (3600 * 10), method: :get)
# => https//corellia.lan/v1/AUTH_39c47bfd3ecd41938368239813628963/destination/death/star.moon?temp_url_sig=cbd7568b60abcd5862a96eb03af5fa154e851d54&temp_url_expires=1439430168
```

## Contributing

Expand Down
3 changes: 0 additions & 3 deletions lib/swift_storage/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,4 @@ def temp_url_key
def relative_path
''
end


end

1 change: 1 addition & 0 deletions lib/swift_storage/headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module Headers
CONTAINER_READ = 'X-Container-Read'.freeze
CONTAINER_WRITE = 'X-Container-Write'.freeze
ACCOUNT_TEMP_URL_KEY = 'X-Account-Meta-Temp-URL-Key'.freeze
DESTINATION = 'Destination'.freeze
end

end
34 changes: 28 additions & 6 deletions lib/swift_storage/object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,29 @@ def write(input_stream=nil,
input_stream
end

# Creates a copy of an object that is already stored in Swift.
#
# @param source [String, SwiftStorage::Object]
# The container and object name of the source object or the SwiftStorage::Object source
#
# @param optional_headers [Hash]
# All optional headers supported by Swift API for object copy
#
# @return [SwiftStorage::Object]
# The current object
def copy_from(source, optional_headers = {})
case source
when SwiftStorage::Object
path = source.relative_path
when String
path = source
else
raise ArgumentError.new('Invalid source type')
end

request(path, method: :copy, headers: optional_headers.merge(H::DESTINATION => relative_path))
self
end

# Generates a public URL with an expiration time
#
Expand Down Expand Up @@ -184,13 +207,12 @@ def url
File.join(service.storage_url, relative_path)
end

private

H = SwiftStorage::Headers

# Returns the object's relative path (container name with object name)
#
# @return [String]
# The object relative path.
#
def relative_path
File.join(container.name, name)
end

end

4 changes: 3 additions & 1 deletion lib/swift_storage/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def create_temp_url(container, object, expires, method, options = {})

string_to_sign = "#{method}\n#{expires}\n#{object_path_unescaped}"

sig = sig_to_hex(hmac('sha1', temp_url_key, string_to_sign))
sig = sig_to_hex(hmac('sha1', temp_url_key, string_to_sign))

klass = scheme == 'http' ? URI::HTTP : URI::HTTPS

Expand Down Expand Up @@ -155,6 +155,8 @@ def request(path_or_url,
req = Net::HTTP::Post.new(path, headers)
when :put
req = Net::HTTP::Put.new(path, headers)
when :copy
req = Net::HTTP::Copy.new(path, headers)
else
raise ArgumentError, "Method #{method} not supported"
end
Expand Down
4 changes: 3 additions & 1 deletion spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ def body(new_body)
def random_length
Random.rand(5000) + 1000
end
end


SwiftStorage.configure do |config|
config.auth_version = '1.0'
end

RSpec::Matchers.define :send_request do |method, path, options={}|
Expand Down
42 changes: 42 additions & 0 deletions spec/swift/object_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,46 @@
expect(subject.metadata.jon_doe).to eq('a meta')
end

describe '#copy_from' do
subject { swift_service.containers['some_destination_container'].objects['some_copied_object'] }
let(:source) { swift_service.containers['some_source_container'].objects['some_object'] }

it 'adds optional headers to request' do
expect { subject.copy_from(source, 'X-Object-Falcon' => 'Awesome') }.to send_request(
:copy,
'/v1/AUTH_test/some_source_container/some_object',
headers: { h::DESTINATION => 'some_destination_container/some_copied_object', 'X-Object-Falcon' => 'Awesome' }
)
end

context 'when source is a SwiftStorage::Object' do
it 'copies the source' do
expect { subject.copy_from(source) }.to send_request(
:copy,
'/v1/AUTH_test/some_source_container/some_object',
headers: { h::DESTINATION => 'some_destination_container/some_copied_object' }
)
end
end

context 'when source is a string' do
it 'copies the source' do
expect { subject.copy_from(source.relative_path) }.to send_request(
:copy,
'/v1/AUTH_test/some_source_container/some_object',
headers: { h::DESTINATION => 'some_destination_container/some_copied_object' }
)
end
end

context 'when source is an integer' do
it 'raises an error' do
expect { subject.copy_from(42) }.to raise_error(ArgumentError, 'Invalid source type')
end
end

it 'returns destination object' do
expect(subject.copy_from(source).relative_path).to eq('some_destination_container/some_copied_object')
end
end
end