fog-softlayer 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/.travis.yml +37 -0
- data/CONTRIBUTING.md +30 -0
- data/README.md +8 -5
- data/docs/cla-corporate.md +133 -0
- data/docs/cla-individual.md +84 -0
- data/docs/fog-softlayer-CCLA.pdf +0 -0
- data/docs/fog-softlayer-CLA.pdf +0 -0
- data/examples/storage.md +107 -0
- data/fog-softlayer.gemspec +1 -0
- data/gemfiles/Gemfile-edge +14 -0
- data/gemfiles/Gemfile-ruby-1.8.7 +14 -0
- data/lib/fog/softlayer.rb +1 -0
- data/lib/fog/softlayer/compute.rb +1 -1
- data/lib/fog/softlayer/compute/shared.rb +1 -1
- data/lib/fog/softlayer/core.rb +17 -1
- data/lib/fog/softlayer/models/storage/directories.rb +40 -0
- data/lib/fog/softlayer/models/storage/directory.rb +50 -0
- data/lib/fog/softlayer/models/storage/file.rb +166 -0
- data/lib/fog/softlayer/models/storage/files.rb +99 -0
- data/lib/fog/softlayer/requests/storage/copy_object.rb +42 -0
- data/lib/fog/softlayer/requests/storage/delete_container.rb +38 -0
- data/lib/fog/softlayer/requests/storage/delete_multiple_objects.rb +67 -0
- data/lib/fog/softlayer/requests/storage/delete_object.rb +40 -0
- data/lib/fog/softlayer/requests/storage/delete_static_large_object.rb +43 -0
- data/lib/fog/softlayer/requests/storage/get_container.rb +68 -0
- data/lib/fog/softlayer/requests/storage/get_containers.rb +51 -0
- data/lib/fog/softlayer/requests/storage/get_object.rb +45 -0
- data/lib/fog/softlayer/requests/storage/get_object_https_url.rb +88 -0
- data/lib/fog/softlayer/requests/storage/head_container.rb +28 -0
- data/lib/fog/softlayer/requests/storage/head_containers.rb +25 -0
- data/lib/fog/softlayer/requests/storage/head_object.rb +23 -0
- data/lib/fog/softlayer/requests/storage/post_set_meta_temp_url_key.rb +37 -0
- data/lib/fog/softlayer/requests/storage/put_container.rb +32 -0
- data/lib/fog/softlayer/requests/storage/put_dynamic_obj_manifest.rb +43 -0
- data/lib/fog/softlayer/requests/storage/put_object.rb +61 -0
- data/lib/fog/softlayer/requests/storage/put_object_manifest.rb +16 -0
- data/lib/fog/softlayer/requests/storage/put_static_obj_manifest.rb +57 -0
- data/lib/fog/softlayer/storage.rb +283 -0
- data/lib/fog/softlayer/version.rb +1 -1
- data/tests/helper.rb +0 -29
- data/tests/helpers/mock_helper.rb +4 -98
- data/tests/softlayer/models/storage/directory_tests.rb +49 -0
- data/tests/softlayer/models/storage/file_tests.rb +56 -0
- data/tests/softlayer/requests/storage/auth_tests.rb +17 -0
- data/tests/softlayer/requests/storage/container_tests.rb +52 -0
- data/tests/softlayer/requests/storage/object_tests.rb +68 -0
- metadata +53 -2
@@ -0,0 +1,42 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Mock
|
5
|
+
def copy_object(source_container, source_object, target_container, target_object, options={})
|
6
|
+
response = Excon::Response.new
|
7
|
+
if @containers[source_container].nil? || @containers[source_container][source_object].nil? || @containers[target_container].nil?
|
8
|
+
response.body = '<html><h1>Not Found</h1><p>The resource could not be found.</p></html>'
|
9
|
+
response.status = 404
|
10
|
+
else # Success
|
11
|
+
@containers[target_container][target_object] = @containers[source_container][source_object]
|
12
|
+
response.body = ''
|
13
|
+
response.status = 201
|
14
|
+
end
|
15
|
+
response
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Real
|
20
|
+
|
21
|
+
# Copy object
|
22
|
+
#
|
23
|
+
# ==== Parameters
|
24
|
+
# * source_container_name<~String> - Name of source bucket
|
25
|
+
# * source_object_name<~String> - Name of source object
|
26
|
+
# * target_container_name<~String> - Name of bucket to create copy in
|
27
|
+
# * target_object_name<~String> - Name for new copy of object
|
28
|
+
# * options<~Hash> - Additional headers
|
29
|
+
def copy_object(source_container, source_object, target_container, target_object, options={})
|
30
|
+
headers = { 'X-Copy-From' => "/#{source_container}/#{source_object}" }.merge(options)
|
31
|
+
request({
|
32
|
+
:expects => 201,
|
33
|
+
:headers => headers,
|
34
|
+
:method => 'PUT',
|
35
|
+
:path => "#{Fog::Softlayer.escape(target_container)}/#{Fog::Softlayer.escape(target_object)}"
|
36
|
+
})
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Mock
|
5
|
+
def delete_container(name)
|
6
|
+
response = Excon::Response.new
|
7
|
+
if @containers[name].nil? # Container doesn't exist.
|
8
|
+
response.body = '<html><h1>Not Found</h1><p>The resource could not be found.</p></html>'
|
9
|
+
response.status = 404
|
10
|
+
elsif @containers[name].length > 0 # Container not empty
|
11
|
+
response.body = '<html><h1>Conflict</h1><p>There was a conflict when trying to complete your request.</p></html>'
|
12
|
+
response.status = 409
|
13
|
+
else # Success
|
14
|
+
response.body = ''
|
15
|
+
response.status = 204
|
16
|
+
end
|
17
|
+
response
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Real
|
22
|
+
# Delete an existing container
|
23
|
+
#
|
24
|
+
# ==== Parameters
|
25
|
+
# * name<~String> - Name of container to delete
|
26
|
+
#
|
27
|
+
def delete_container(name)
|
28
|
+
request(
|
29
|
+
:expects => 204,
|
30
|
+
:method => 'DELETE',
|
31
|
+
:path => Fog::Softlayer.escape(name)
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Real
|
5
|
+
|
6
|
+
# Deletes multiple objects or containers with a single request.
|
7
|
+
#
|
8
|
+
# To delete objects from a single container, +container+ may be provided
|
9
|
+
# and +object_names+ should be an Array of object names within the container.
|
10
|
+
#
|
11
|
+
# To delete objects from multiple containers or delete containers,
|
12
|
+
# +container+ should be +nil+ and all +object_names+ should be prefixed with a container name.
|
13
|
+
#
|
14
|
+
# Containers must be empty when deleted. +object_names+ are processed in the order given,
|
15
|
+
# so objects within a container should be listed first to empty the container.
|
16
|
+
#
|
17
|
+
# Up to 10,000 objects may be deleted in a single request.
|
18
|
+
# The server will respond with +200 OK+ for all requests.
|
19
|
+
# +response.body+ must be inspected for actual results.
|
20
|
+
#
|
21
|
+
# @example Delete objects from a container
|
22
|
+
# object_names = ['object', 'another/object']
|
23
|
+
# conn.delete_multiple_objects('my_container', object_names)
|
24
|
+
#
|
25
|
+
# @example Delete objects from multiple containers
|
26
|
+
# object_names = ['container_a/object', 'container_b/object']
|
27
|
+
# conn.delete_multiple_objects(nil, object_names)
|
28
|
+
#
|
29
|
+
# @example Delete a container and all it's objects
|
30
|
+
# object_names = ['my_container/object_a', 'my_container/object_b', 'my_container']
|
31
|
+
# conn.delete_multiple_objects(nil, object_names)
|
32
|
+
#
|
33
|
+
# @param container [String,nil] Name of container.
|
34
|
+
# @param object_names [Array<String>] Object names to be deleted.
|
35
|
+
# @param options [Hash] Additional request headers.
|
36
|
+
#
|
37
|
+
# @return [Excon::Response]
|
38
|
+
# * body [Hash] - Results of the operation.
|
39
|
+
# * "Number Not Found" [Integer] - Number of missing objects or containers.
|
40
|
+
# * "Response Status" [String] - Response code for the subrequest of the last failed operation.
|
41
|
+
# * "Errors" [Array<object_name, response_status>]
|
42
|
+
# * object_name [String] - Object that generated an error when the delete was attempted.
|
43
|
+
# * response_status [String] - Response status from the subrequest for object_name.
|
44
|
+
# * "Number Deleted" [Integer] - Number of objects or containers deleted.
|
45
|
+
# * "Response Body" [String] - Response body for "Response Status".
|
46
|
+
def delete_multiple_objects(container, object_names, options = {})
|
47
|
+
body = object_names.map do |name|
|
48
|
+
object_name = container ? "#{ container }/#{ name }" : name
|
49
|
+
URI.encode(object_name)
|
50
|
+
end.join("\n")
|
51
|
+
|
52
|
+
response = request({
|
53
|
+
:expects => 200,
|
54
|
+
:method => 'DELETE',
|
55
|
+
:headers => options.merge('Content-Type' => 'text/plain',
|
56
|
+
'Accept' => 'application/json'),
|
57
|
+
:body => body,
|
58
|
+
:query => { 'bulk-delete' => true }
|
59
|
+
}, false)
|
60
|
+
response.body = Fog::JSON.decode(response.body)
|
61
|
+
response
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Mock
|
5
|
+
|
6
|
+
def delete_object(container, object)
|
7
|
+
response = Excon::Response.new
|
8
|
+
if @containers[container].nil? || @containers[container][object].nil? # Container or object doesn't exist.
|
9
|
+
response.body = '<html><h1>Not Found</h1><p>The resource could not be found.</p></html>'
|
10
|
+
response.status = 404
|
11
|
+
else # Success
|
12
|
+
response.body = ''
|
13
|
+
response.status = 204
|
14
|
+
end
|
15
|
+
response
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
class Real
|
22
|
+
|
23
|
+
# Delete an existing object
|
24
|
+
#
|
25
|
+
# ==== Parameters
|
26
|
+
# * container<~String> - Name of container to delete
|
27
|
+
# * object<~String> - Name of object to delete
|
28
|
+
#
|
29
|
+
def delete_object(container, object)
|
30
|
+
request(
|
31
|
+
:expects => 204,
|
32
|
+
:method => 'DELETE',
|
33
|
+
:path => "#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object)}"
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Real
|
5
|
+
|
6
|
+
# Delete a static large object.
|
7
|
+
#
|
8
|
+
# Deletes the SLO manifest +object+ and all segments that it references.
|
9
|
+
# The server will respond with +200 OK+ for all requests.
|
10
|
+
# +response.body+ must be inspected for actual results.
|
11
|
+
#
|
12
|
+
# @param container [String] Name of container.
|
13
|
+
# @param object [String] Name of the SLO manifest object.
|
14
|
+
# @param options [Hash] Additional request headers.
|
15
|
+
#
|
16
|
+
# @return [Excon::Response]
|
17
|
+
# * body [Hash] - Results of the operation.
|
18
|
+
# * "Number Not Found" [Integer] - Number of missing segments.
|
19
|
+
# * "Response Status" [String] - Response code for the subrequest of the last failed operation.
|
20
|
+
# * "Errors" [Array<object_name, response_status>]
|
21
|
+
# * object_name [String] - Object that generated an error when the delete was attempted.
|
22
|
+
# * response_status [String] - Response status from the subrequest for object_name.
|
23
|
+
# * "Number Deleted" [Integer] - Number of segments deleted.
|
24
|
+
# * "Response Body" [String] - Response body for Response Status.
|
25
|
+
#
|
26
|
+
# @see http://docs.openstack.org/api/openstack-object-storage/1.0/content/static-large-objects.html
|
27
|
+
def delete_static_large_object(container, object, options = {})
|
28
|
+
response = request({
|
29
|
+
:expects => 200,
|
30
|
+
:method => 'DELETE',
|
31
|
+
:headers => options.merge('Content-Type' => 'text/plain',
|
32
|
+
'Accept' => 'application/json'),
|
33
|
+
:path => "#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object)}",
|
34
|
+
:query => { 'multipart-manifest' => 'delete' }
|
35
|
+
}, false)
|
36
|
+
response.body = Fog::JSON.decode(response.body)
|
37
|
+
response
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Mock
|
5
|
+
def get_container(container, options = {})
|
6
|
+
if @containers[container]
|
7
|
+
response = Excon::Response.new
|
8
|
+
response.body = @containers[container].map do |name, object|
|
9
|
+
{
|
10
|
+
'hash' => object.respond_to?(:to_s) ? Digest::MD5.hexdigest(object.to_s) : 'e4d909c290d0fb1ca068ffaddf22cbd0',
|
11
|
+
'last_modified' => Time.now,
|
12
|
+
'bytes' => Memory.analyze(container).bytes,
|
13
|
+
'content/type' => 'application/json'
|
14
|
+
}
|
15
|
+
end
|
16
|
+
response.status = 200
|
17
|
+
response
|
18
|
+
else
|
19
|
+
response = Excon::Response.new
|
20
|
+
response.body = '<html><h1>Not Found</h1><p>The resource could not be found.</p></html>'
|
21
|
+
response.status = 404
|
22
|
+
response.headers = {"Content-Length"=>"70", "Content-Type"=>"text/html; charset=UTF-8", "X-Trans-Id"=>"abcdefghijklmnopqrstuvwx-0123456789", "Date"=>Time.now}
|
23
|
+
response
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Real
|
29
|
+
|
30
|
+
# Get details for container and total bytes stored
|
31
|
+
#
|
32
|
+
# ==== Parameters
|
33
|
+
# * container<~String> - Name of container to retrieve info for
|
34
|
+
# * options<~String>:
|
35
|
+
# * 'limit'<~String> - Maximum number of objects to return
|
36
|
+
# * 'marker'<~String> - Only return objects whose name is greater than marker
|
37
|
+
# * 'prefix'<~String> - Limits results to those starting with prefix
|
38
|
+
# * 'path'<~String> - Return objects nested in the pseudo path
|
39
|
+
#
|
40
|
+
# ==== Returns
|
41
|
+
# * response<~Excon::Response>:
|
42
|
+
# * headers<~Hash>:
|
43
|
+
# * 'X-Account-Container-Count'<~String> - Count of containers
|
44
|
+
# * 'X-Account-Bytes-Used'<~String> - Bytes used
|
45
|
+
# * body<~Array>:
|
46
|
+
# * 'bytes'<~Integer> - Number of bytes used by container
|
47
|
+
# * 'count'<~Integer> - Number of items in container
|
48
|
+
# * 'name'<~String> - Name of container
|
49
|
+
# * item<~Hash>:
|
50
|
+
# * 'bytes'<~String> - Size of object
|
51
|
+
# * 'content_type'<~String> Content-Type of object
|
52
|
+
# * 'hash'<~String> - Hash of object (etag?)
|
53
|
+
# * 'last_modified'<~String> - Last modified timestamp
|
54
|
+
# * 'name'<~String> - Name of object
|
55
|
+
def get_container(container, options = {})
|
56
|
+
options = options.reject {|key, value| value.nil?}
|
57
|
+
request(
|
58
|
+
:expects => 200,
|
59
|
+
:method => 'GET',
|
60
|
+
:path => Fog::Softlayer.escape(container),
|
61
|
+
:query => {'format' => 'json'}.merge!(options)
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Mock
|
5
|
+
def get_containers(options = {})
|
6
|
+
response = Excon::Response.new
|
7
|
+
response.body = _format_containers(@containers)
|
8
|
+
response.status = 200
|
9
|
+
response
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def _format_containers(containers)
|
15
|
+
containers.map do |name, container|
|
16
|
+
meta = Memory.analyze(container)
|
17
|
+
{'count' => container.length, 'bytes' => meta.bytes, 'name' => name}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Real
|
23
|
+
|
24
|
+
# List existing storage containers
|
25
|
+
#
|
26
|
+
# ==== Parameters
|
27
|
+
# * options<~Hash>:
|
28
|
+
# * 'limit'<~Integer> - Upper limit to number of results returned
|
29
|
+
# * 'marker'<~String> - Only return objects with name greater than this value
|
30
|
+
#
|
31
|
+
# ==== Returns
|
32
|
+
# * response<~Excon::Response>:
|
33
|
+
# * body<~Array>:
|
34
|
+
# * container<~Hash>:
|
35
|
+
# * 'bytes'<~Integer>: - Number of bytes used by container
|
36
|
+
# * 'count'<~Integer>: - Number of items in container
|
37
|
+
# * 'name'<~String>: - Name of container
|
38
|
+
def get_containers(options = {})
|
39
|
+
options = options.reject {|key, value| value.nil?}
|
40
|
+
request(
|
41
|
+
:expects => [200, 204],
|
42
|
+
:method => 'GET',
|
43
|
+
:path => '',
|
44
|
+
:query => {'format' => 'json'}.merge!(options)
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Mock
|
5
|
+
def get_object(container, object, &block)
|
6
|
+
if @containers[container] && @containers[container][object]
|
7
|
+
response = Excon::Response.new
|
8
|
+
response.body = @containers[container][object]
|
9
|
+
response.status = 200
|
10
|
+
response
|
11
|
+
else
|
12
|
+
response = Excon::Response.new
|
13
|
+
response.body = '<html><h1>Not Found</h1><p>The resource could not be found.</p></html>'
|
14
|
+
response.status = 404
|
15
|
+
response.headers = {"Content-Length"=>"70", "Content-Type"=>"text/html; charset=UTF-8", "X-Trans-Id"=>"abcdefghijklmnopqrstuvwx-0123456789", "Date"=>Time.now}
|
16
|
+
response
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
class Real
|
21
|
+
|
22
|
+
# Get details for object
|
23
|
+
#
|
24
|
+
# ==== Parameters
|
25
|
+
# * container<~String> - Name of container to look in
|
26
|
+
# * object<~String> - Name of object to look for
|
27
|
+
#
|
28
|
+
def get_object(container, object, &block)
|
29
|
+
params = {
|
30
|
+
:expects => 200,
|
31
|
+
:method => 'GET',
|
32
|
+
:path => "#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object)}"
|
33
|
+
}
|
34
|
+
|
35
|
+
if block_given?
|
36
|
+
params[:response_block] = block
|
37
|
+
end
|
38
|
+
|
39
|
+
request(params, false)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Mock
|
5
|
+
def get_object_https_url(container, object, expires, options = {})
|
6
|
+
"https://cluster.objectstorage.softlayer.net:443/v1/AUTH_abcdefghijklmnopqrstuvwxyz/#{container}/#{object}?temp_url_sig=1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a&temp_url_expires=1111111111111"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Real
|
11
|
+
|
12
|
+
# Get an expiring object https url from Cloud Files
|
13
|
+
#
|
14
|
+
# ==== Parameters
|
15
|
+
# * container<~String> - Name of container containing object
|
16
|
+
# * object<~String> - Name of object to get expiring url for
|
17
|
+
# * expires<~Time> - An expiry time for this url
|
18
|
+
#
|
19
|
+
# ==== Returns
|
20
|
+
# * response<~Excon::Response>:
|
21
|
+
# * body<~String> - url for object
|
22
|
+
def get_object_https_url(container, object, expires, options = {})
|
23
|
+
create_temp_url(container, object, expires, "GET", options.merge(:scheme => "https"))
|
24
|
+
end
|
25
|
+
|
26
|
+
# creates a temporary url
|
27
|
+
#
|
28
|
+
# ==== Parameters
|
29
|
+
# * container<~String> - Name of container containing object
|
30
|
+
# * object<~String> - Name of object to get expiring url for
|
31
|
+
# * expires<~Time> - An expiry time for this url
|
32
|
+
# * method<~String> - The method to use for accessing the object (GET, PUT, HEAD)
|
33
|
+
# * scheme<~String> - The scheme to use (http, https)
|
34
|
+
# * options<~Hash> - An optional options hash
|
35
|
+
#
|
36
|
+
# ==== Returns
|
37
|
+
# * response<~Excon::Response>:
|
38
|
+
# * body<~String> - url for object
|
39
|
+
#
|
40
|
+
def create_temp_url(container, object, expires, method, options = {})
|
41
|
+
raise ArgumentError, "Insufficient parameters specified." unless (container && object && expires && method)
|
42
|
+
raise ArgumentError, "Storage must be instantiated with the :temp_url_key option" if @temp_url_key.nil?
|
43
|
+
|
44
|
+
scheme = options[:scheme] || @scheme
|
45
|
+
|
46
|
+
# POST not allowed
|
47
|
+
allowed_methods = %w{GET PUT HEAD}
|
48
|
+
unless allowed_methods.include?(method)
|
49
|
+
raise ArgumentError.new("Invalid method '#{method}' specified. Valid methods are: #{allowed_methods.join(', ')}")
|
50
|
+
end
|
51
|
+
|
52
|
+
expires = expires.to_i
|
53
|
+
object_path_escaped = "#{@path}/#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object,"/")}"
|
54
|
+
object_path_unescaped = "#{@path}/#{Fog::Softlayer.escape(container)}/#{object}"
|
55
|
+
string_to_sign = "#{method}\n#{expires}\n#{object_path_unescaped}"
|
56
|
+
|
57
|
+
hmac = Fog::HMAC.new('sha1', @temp_url_key)
|
58
|
+
sig = sig_to_hex(hmac.sign(string_to_sign))
|
59
|
+
|
60
|
+
temp_url_options = {
|
61
|
+
:scheme => scheme,
|
62
|
+
:host => @host,
|
63
|
+
:port => @port,
|
64
|
+
:path => object_path_escaped,
|
65
|
+
:query => URI.encode_www_form(
|
66
|
+
:temp_url_sig => sig,
|
67
|
+
:temp_url_expires => expires
|
68
|
+
)
|
69
|
+
}
|
70
|
+
|
71
|
+
URI::Generic.build(temp_url_options).to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def sig_to_hex(str)
|
77
|
+
str.unpack("C*").map { |c|
|
78
|
+
c.to_s(16)
|
79
|
+
}.map { |h|
|
80
|
+
h.size == 1 ? "0#{h}" : h
|
81
|
+
}.join
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|