fog-softlayer 0.0.5 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- 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,28 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Real
|
5
|
+
|
6
|
+
# List number of objects and total bytes stored
|
7
|
+
#
|
8
|
+
# ==== Parameters
|
9
|
+
# * container<~String> - Name of container to retrieve info for
|
10
|
+
#
|
11
|
+
# ==== Returns
|
12
|
+
# * response<~Excon::Response>:
|
13
|
+
# * headers<~Hash>:
|
14
|
+
# * 'X-Container-Object-Count'<~String> - Count of containers
|
15
|
+
# * 'X-Container-Bytes-Used'<~String> - Bytes used
|
16
|
+
def head_container(container)
|
17
|
+
request(
|
18
|
+
:expects => 204,
|
19
|
+
:method => 'HEAD',
|
20
|
+
:path => Fog::Softlayer.escape(container),
|
21
|
+
:query => {'format' => 'json'}
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Real
|
5
|
+
|
6
|
+
# List number of containers and total bytes stored
|
7
|
+
#
|
8
|
+
# ==== Returns
|
9
|
+
# * response<~Excon::Response>:
|
10
|
+
# * headers<~Hash>:
|
11
|
+
# * 'X-Account-Container-Count'<~String> - Count of containers
|
12
|
+
# * 'X-Account-Bytes-Used'<~String> - Bytes used
|
13
|
+
def head_containers
|
14
|
+
request(
|
15
|
+
:expects => 204,
|
16
|
+
:method => 'HEAD',
|
17
|
+
:path => '',
|
18
|
+
:query => {'format' => 'json'}
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Real
|
5
|
+
|
6
|
+
# Get headers for object
|
7
|
+
#
|
8
|
+
# ==== Parameters
|
9
|
+
# * container<~String> - Name of container to look in
|
10
|
+
# * object<~String> - Name of object to look for
|
11
|
+
#
|
12
|
+
def head_object(container, object)
|
13
|
+
request({
|
14
|
+
:expects => 200,
|
15
|
+
:method => 'HEAD',
|
16
|
+
:path => "#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object)}"
|
17
|
+
}, false)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
|
5
|
+
class Real
|
6
|
+
|
7
|
+
# Set the account wide Temp URL Key. This is a secret key that's
|
8
|
+
# used to generate signed expiring URLs.
|
9
|
+
#
|
10
|
+
# Once the key has been set with this request you should create new
|
11
|
+
# Storage objects with the :temp_url_key option then use
|
12
|
+
# the get_object_https_url method to generate expiring URLs.
|
13
|
+
#
|
14
|
+
# *** CAUTION *** changing this secret key will invalidate any expiring
|
15
|
+
# URLS generated with old keys.
|
16
|
+
#
|
17
|
+
# ==== Parameters
|
18
|
+
# * key<~String> - The new Temp URL Key
|
19
|
+
#
|
20
|
+
# ==== Returns
|
21
|
+
# * response<~Excon::Response>
|
22
|
+
#
|
23
|
+
# ==== See Also
|
24
|
+
# http://docs.rackspace.com/files/api/v1/cf-devguide/content/Set_Account_Metadata-d1a4460.html
|
25
|
+
def post_set_meta_temp_url_key(key)
|
26
|
+
request(
|
27
|
+
:expects => [201, 202, 204],
|
28
|
+
:method => 'POST',
|
29
|
+
:headers => {'X-Account-Meta-Temp-Url-Key' => key}
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Mock
|
5
|
+
def put_container(name)
|
6
|
+
@containers[name] = {} unless @containers[name]
|
7
|
+
response = Excon::Response.new
|
8
|
+
response.body = ''
|
9
|
+
response.status = 201
|
10
|
+
response
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Real
|
15
|
+
|
16
|
+
# Create a new container
|
17
|
+
#
|
18
|
+
# ==== Parameters
|
19
|
+
# * name<~String> - Name for container, should be < 256 bytes and must not contain '/'
|
20
|
+
#
|
21
|
+
def put_container(name)
|
22
|
+
request(
|
23
|
+
:expects => [201, 202],
|
24
|
+
:method => 'PUT',
|
25
|
+
:path => Fog::Softlayer.escape(name)
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Real
|
5
|
+
|
6
|
+
# Create a new dynamic large object manifest
|
7
|
+
#
|
8
|
+
# Creates an object with a +X-Object-Manifest+ header that specifies the common prefix ("<container>/<prefix>")
|
9
|
+
# for all uploaded segments. Retrieving the manifest object streams all segments matching this prefix.
|
10
|
+
# Segments must sort in the order they should be concatenated. Note that any future objects stored in the container
|
11
|
+
# along with the segments that match the prefix will be included when retrieving the manifest object.
|
12
|
+
#
|
13
|
+
# All segments must be stored in the same container, but may be in a different container than the manifest object.
|
14
|
+
# The default +X-Object-Manifest+ header is set to "+container+/+object+", but may be overridden in +options+
|
15
|
+
# to specify the prefix and/or the container where segments were stored.
|
16
|
+
# If overridden, names should be CGI escaped (excluding spaces) if needed (see {Fog::Softlayer.escape}).
|
17
|
+
#
|
18
|
+
# @param container [String] Name for container where +object+ will be stored. Should be < 256 bytes and must not contain '/'
|
19
|
+
# @param object [String] Name for manifest object.
|
20
|
+
# @param options [Hash] Config headers for +object+.
|
21
|
+
# @option options [String] 'X-Object-Manifest' ("container/object") "<container>/<prefix>" for segment objects.
|
22
|
+
#
|
23
|
+
# @raise [Fog::Storage::Softlayer::NotFound] HTTP 404
|
24
|
+
# @raise [Excon::Errors::BadRequest] HTTP 400
|
25
|
+
# @raise [Excon::Errors::Unauthorized] HTTP 401
|
26
|
+
# @raise [Excon::Errors::HTTPStatusError]
|
27
|
+
#
|
28
|
+
# @see http://docs.openstack.org/api/openstack-object-storage/1.0/content/dynamic-large-object-creation.html
|
29
|
+
def put_dynamic_obj_manifest(container, object, options = {})
|
30
|
+
path = "#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object)}"
|
31
|
+
headers = {'X-Object-Manifest' => path}.merge(options)
|
32
|
+
request(
|
33
|
+
:expects => 201,
|
34
|
+
:headers => headers,
|
35
|
+
:method => 'PUT',
|
36
|
+
:path => path
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Mock
|
5
|
+
def put_object(container, object, data, options = {}, &block)
|
6
|
+
if @containers[container]
|
7
|
+
@containers[container][object] = data
|
8
|
+
response = Excon::Response.new
|
9
|
+
response.body = ''
|
10
|
+
response.status = 201
|
11
|
+
response.headers = {"Last-Modified"=>Time.now, "Content-Length"=>0}
|
12
|
+
response
|
13
|
+
else
|
14
|
+
response = Excon::Response.new
|
15
|
+
response.body = '<html><h1>Not Found</h1><p>The resource could not be found.</p></html>'
|
16
|
+
response.status = 404
|
17
|
+
response.headers = {"Content-Length"=>"70", "Content-Type"=>"text/html; charset=UTF-8", "X-Trans-Id"=>"abcdefghijklmnopqrstuvwx-0123456789", "Date"=>Time.now}
|
18
|
+
response
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Real
|
24
|
+
|
25
|
+
# Create a new object
|
26
|
+
#
|
27
|
+
# When passed a block, it will make a chunked request, calling
|
28
|
+
# the block for chunks until it returns an empty string.
|
29
|
+
# In this case the data parameter is ignored.
|
30
|
+
#
|
31
|
+
# ==== Parameters
|
32
|
+
# * container<~String> - Name for container, should be < 256 bytes and must not contain '/'
|
33
|
+
# * object<~String> - Name for object
|
34
|
+
# * data<~String|File> - data to upload
|
35
|
+
# * options<~Hash> - config headers for object. Defaults to {}.
|
36
|
+
# * block<~Proc> - chunker
|
37
|
+
#
|
38
|
+
def put_object(container, object, data, options = {}, &block)
|
39
|
+
if block_given?
|
40
|
+
params = { :request_block => block }
|
41
|
+
headers = options
|
42
|
+
else
|
43
|
+
data = Fog::Storage.parse_data(data)
|
44
|
+
headers = data[:headers].merge!(options)
|
45
|
+
params = { :body => data[:body] }
|
46
|
+
end
|
47
|
+
|
48
|
+
params.merge!(
|
49
|
+
:expects => 201,
|
50
|
+
:idempotent => !params[:request_block],
|
51
|
+
:headers => headers,
|
52
|
+
:method => 'PUT',
|
53
|
+
:path => "#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object)}"
|
54
|
+
)
|
55
|
+
|
56
|
+
request(params)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Real
|
5
|
+
|
6
|
+
# Create a new dynamic large object manifest
|
7
|
+
#
|
8
|
+
# This is an alias for {#put_dynamic_obj_manifest} for backward compatibility.
|
9
|
+
def put_object_manifest(container, object, options = {})
|
10
|
+
put_dynamic_obj_manifest(container, object, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Softlayer
|
4
|
+
class Real
|
5
|
+
|
6
|
+
# Create a new static large object manifest.
|
7
|
+
#
|
8
|
+
# A static large object is similar to a dynamic large object. Whereas a GET for a dynamic large object manifest
|
9
|
+
# will stream segments based on the manifest's +X-Object-Manifest+ object name prefix, a static large object
|
10
|
+
# manifest streams segments which are defined by the user within the manifest. Information about each segment is
|
11
|
+
# provided in +segments+ as an Array of Hash objects, ordered in the sequence which the segments should be streamed.
|
12
|
+
#
|
13
|
+
# When the SLO manifest is received, each segment's +etag+ and +size_bytes+ will be verified.
|
14
|
+
# The +etag+ for each segment is returned in the response to {#put_object}, but may also be calculated.
|
15
|
+
# e.g. +Digest::MD5.hexdigest(segment_data)+
|
16
|
+
#
|
17
|
+
# The maximum number of segments for a static large object is 1000, and all segments (except the last) must be
|
18
|
+
# at least 1 MiB in size. Unlike a dynamic large object, segments are not required to be in the same container.
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# segments = [
|
22
|
+
# { :path => 'segments_container/first_segment',
|
23
|
+
# :etag => 'md5 for first_segment',
|
24
|
+
# :size_bytes => 'byte size of first_segment' },
|
25
|
+
# { :path => 'segments_container/second_segment',
|
26
|
+
# :etag => 'md5 for second_segment',
|
27
|
+
# :size_bytes => 'byte size of second_segment' }
|
28
|
+
# ]
|
29
|
+
# put_static_obj_manifest('my_container', 'my_large_object', segments)
|
30
|
+
#
|
31
|
+
# @param container [String] Name for container where +object+ will be stored.
|
32
|
+
# Should be < 256 bytes and must not contain '/'
|
33
|
+
# @param object [String] Name for manifest object.
|
34
|
+
# @param segments [Array<Hash>] Segment data for the object.
|
35
|
+
# @param options [Hash] Config headers for +object+.
|
36
|
+
#
|
37
|
+
# @raise [Fog::Storage::Softlayer::NotFound] HTTP 404
|
38
|
+
# @raise [Excon::Errors::BadRequest] HTTP 400
|
39
|
+
# @raise [Excon::Errors::Unauthorized] HTTP 401
|
40
|
+
# @raise [Excon::Errors::HTTPStatusError]
|
41
|
+
#
|
42
|
+
# @see http://docs.openstack.org/api/openstack-object-storage/1.0/content/static-large-objects.html
|
43
|
+
def put_static_obj_manifest(container, object, segments, options = {})
|
44
|
+
request(
|
45
|
+
:expects => 201,
|
46
|
+
:method => 'PUT',
|
47
|
+
:headers => options,
|
48
|
+
:body => Fog::JSON.encode(segments),
|
49
|
+
:path => "#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object)}",
|
50
|
+
:query => { 'multipart-manifest' => 'put' }
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,283 @@
|
|
1
|
+
require 'fog/softlayer/core'
|
2
|
+
|
3
|
+
module Fog
|
4
|
+
module Storage
|
5
|
+
class Softlayer < Fog::Service
|
6
|
+
requires :softlayer_username, :softlayer_api_key, :softlayer_cluster
|
7
|
+
recognizes :persistent, :softlayer_storage_account, :softlayer_temp_url_key
|
8
|
+
|
9
|
+
model_path 'fog/softlayer/models/storage'
|
10
|
+
model :directory
|
11
|
+
collection :directories
|
12
|
+
model :file
|
13
|
+
collection :files
|
14
|
+
|
15
|
+
request_path 'fog/softlayer/requests/storage'
|
16
|
+
request :copy_object
|
17
|
+
request :delete_container
|
18
|
+
request :delete_object
|
19
|
+
request :delete_multiple_objects
|
20
|
+
request :delete_static_large_object
|
21
|
+
request :get_container
|
22
|
+
request :get_containers
|
23
|
+
request :get_object
|
24
|
+
request :get_object_https_url
|
25
|
+
request :head_container
|
26
|
+
request :head_containers
|
27
|
+
request :head_object
|
28
|
+
request :put_container
|
29
|
+
request :put_object
|
30
|
+
request :put_object_manifest
|
31
|
+
request :put_dynamic_obj_manifest
|
32
|
+
request :put_static_obj_manifest
|
33
|
+
request :post_set_meta_temp_url_key
|
34
|
+
|
35
|
+
class Mock
|
36
|
+
|
37
|
+
def self.data
|
38
|
+
@data ||= Hash.new do |hash, key|
|
39
|
+
hash[key] = {}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.reset
|
44
|
+
@data = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize(options={})
|
48
|
+
@softlayer_api_key = options[:softlayer_api_key]
|
49
|
+
@softlayer_username = options[:softlayer_username]
|
50
|
+
@path = '/v1/AUTH_1234'
|
51
|
+
@containers = {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def data
|
55
|
+
self.class.data[@softlayer_username]
|
56
|
+
end
|
57
|
+
|
58
|
+
def reset_data
|
59
|
+
self.class.data.delete(@softlayer_username)
|
60
|
+
end
|
61
|
+
|
62
|
+
def change_account(account)
|
63
|
+
@original_path ||= @path
|
64
|
+
version_string = @original_path.split('/')[1]
|
65
|
+
@path = "/#{version_string}/#{account}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def reset_account_name
|
69
|
+
@path = @original_path
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
class Real
|
75
|
+
attr_reader :auth_url
|
76
|
+
attr_accessor :auth_token, :auth_expires
|
77
|
+
|
78
|
+
def initialize(options={})
|
79
|
+
@api_key = options[:softlayer_api_key]
|
80
|
+
@username = options[:softlayer_username]
|
81
|
+
@cluster = options[:softlayer_cluster]
|
82
|
+
@storage_account = options[:softlayer_storage_account] || default_storage_account
|
83
|
+
@connection_options = options[:connection_options] || {}
|
84
|
+
authenticate
|
85
|
+
@persistent = options[:persistent] || false
|
86
|
+
@connection = Fog::Core::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent, @connection_options)
|
87
|
+
@temp_url_key = options[:softlayer_temp_url_key] || get_temp_url_key_for_account
|
88
|
+
end
|
89
|
+
|
90
|
+
def auth_url
|
91
|
+
"https://#{@cluster}.#{Fog::Softlayer::SL_STORAGE_AUTH_URL}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def reload
|
95
|
+
@connection.reset
|
96
|
+
end
|
97
|
+
|
98
|
+
def request(params = {}, parse_json = true)
|
99
|
+
begin
|
100
|
+
params.is_a?(Hash) or raise ArgumentError, "#{self.class}#request params must be a Hash"
|
101
|
+
params = _build_params(params)
|
102
|
+
response = @connection.request(params)
|
103
|
+
|
104
|
+
if response.status == 401 && !!@auth_token
|
105
|
+
@auth_token = nil; @auth_expires = nil
|
106
|
+
authenticate
|
107
|
+
response = @connection.request(params)
|
108
|
+
end
|
109
|
+
|
110
|
+
if !response.body.empty? && parse_json && response.get_header('Content-Type') =~ %r{application/json}
|
111
|
+
response.body = Fog::JSON.decode(response.body)
|
112
|
+
end
|
113
|
+
|
114
|
+
response
|
115
|
+
rescue Excon::Errors::HTTPStatusError => error
|
116
|
+
raise case error
|
117
|
+
when Excon::Errors::NotFound
|
118
|
+
Fog::Storage::Softlayer::NotFound.slurp(error)
|
119
|
+
else
|
120
|
+
error
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def _auth_headers
|
128
|
+
{
|
129
|
+
:headers => {
|
130
|
+
'User-Agent' => "Fog SoftLayer Adapter #{Fog::Softlayer::VERSION}",
|
131
|
+
'X-Auth-User' => "#{@storage_account}:#{@username}",
|
132
|
+
'X-Auth-Key' => @api_key
|
133
|
+
}
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
def _build_params(params)
|
138
|
+
output = {
|
139
|
+
:method => params.delete(:method) || :get
|
140
|
+
}
|
141
|
+
|
142
|
+
output[:path] = params[:path] ? "#{@path}/#{params.delete(:path)}".sub(/\/$/, '') : @path
|
143
|
+
|
144
|
+
output = output.deep_merge(params)
|
145
|
+
output.deep_merge(_headers)
|
146
|
+
end
|
147
|
+
|
148
|
+
def _headers
|
149
|
+
{
|
150
|
+
:headers => {
|
151
|
+
'Content-Type' => 'application/json',
|
152
|
+
'Accept' => 'application/json',
|
153
|
+
'X-Auth-Token' => @auth_token
|
154
|
+
}
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
def authenticate
|
159
|
+
if requires_auth?
|
160
|
+
connection = Fog::Core::Connection.new(auth_url, false, _auth_headers)
|
161
|
+
response = connection.request(:method => :get)
|
162
|
+
|
163
|
+
raise Fog::Errors::Error.new("Could not authenticate Object Storage User.") unless response.status.between?(200, 208)
|
164
|
+
|
165
|
+
@auth_token = response.headers['X-Auth-Token']
|
166
|
+
@auth_expires = Time.now + response.headers['X-Auth-Token-Expires'].to_i
|
167
|
+
@storage_token = response.headers['X-Storage-Token']
|
168
|
+
|
169
|
+
uri = URI.parse(response.headers['X-Storage-Url'])
|
170
|
+
@host = uri.host
|
171
|
+
@path = uri.path
|
172
|
+
@path.sub!(/\/$/, '')
|
173
|
+
@port = uri.port
|
174
|
+
@scheme = uri.scheme
|
175
|
+
end
|
176
|
+
true
|
177
|
+
end
|
178
|
+
|
179
|
+
def default_storage_account
|
180
|
+
slapi = Fog::Compute[:softlayer].request(:account, :get_hub_network_storage)
|
181
|
+
slapi.body.map { |store| store['username'] }.first if slapi.body and slapi.body.instance_of? Array
|
182
|
+
end
|
183
|
+
|
184
|
+
def get_temp_url_key_for_account
|
185
|
+
request.headers['X-Account-Meta-Temp-Url-Key']
|
186
|
+
end
|
187
|
+
|
188
|
+
def requires_auth?
|
189
|
+
!@auth_token || !@auth_expires || (@auth_expires.to_i - Time.now.to_i) < 30
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
|
196
|
+
# Thanks to @camertron! https://gist.github.com/camertron/2939093
|
197
|
+
module Memory
|
198
|
+
# sizes are a guess, close enough for Mocks
|
199
|
+
REF_SIZE = 4 # ?
|
200
|
+
OBJ_OVERHEAD = 4 # ?
|
201
|
+
FIXNUM_SIZE = 4 # ?
|
202
|
+
|
203
|
+
# informational output from analysis
|
204
|
+
MemoryInfo = Struct.new :roots, :objects, :bytes, :loops
|
205
|
+
|
206
|
+
def self.analyze(*roots)
|
207
|
+
an = Analyzer.new
|
208
|
+
an.roots = roots
|
209
|
+
an.analyze
|
210
|
+
end
|
211
|
+
|
212
|
+
class Analyzer
|
213
|
+
attr_accessor :roots
|
214
|
+
attr_reader :result
|
215
|
+
|
216
|
+
def analyze
|
217
|
+
@result = MemoryInfo.new roots, 0, 0, 0
|
218
|
+
@objs = {}
|
219
|
+
|
220
|
+
queue = roots.dup
|
221
|
+
|
222
|
+
until queue.empty?
|
223
|
+
obj = queue.shift
|
224
|
+
|
225
|
+
case obj
|
226
|
+
when IO
|
227
|
+
visit(obj)
|
228
|
+
when String
|
229
|
+
visit(obj) { @result.bytes += obj.size }
|
230
|
+
when Fixnum
|
231
|
+
@result.bytes += FIXNUM_SIZE
|
232
|
+
when Array
|
233
|
+
visit(obj) do
|
234
|
+
@result.bytes += obj.size * REF_SIZE
|
235
|
+
queue.concat(obj)
|
236
|
+
end
|
237
|
+
when Hash
|
238
|
+
visit(obj) do
|
239
|
+
@result.bytes += obj.size * REF_SIZE * 2
|
240
|
+
obj.each {|k,v| queue.push(k).push(v)}
|
241
|
+
end
|
242
|
+
when Enumerable
|
243
|
+
visit(obj) do
|
244
|
+
obj.each do |o|
|
245
|
+
@result.bytes += REF_SIZE
|
246
|
+
queue.push(o)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
else
|
250
|
+
visit(obj) do
|
251
|
+
obj.instance_variables.each do |var|
|
252
|
+
@result.bytes += REF_SIZE
|
253
|
+
queue.push(obj.instance_variable_get(var))
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
@result
|
260
|
+
end
|
261
|
+
|
262
|
+
private
|
263
|
+
def visit(obj)
|
264
|
+
id = obj.object_id
|
265
|
+
|
266
|
+
if @objs.has_key? id
|
267
|
+
@result.loops += 1
|
268
|
+
false
|
269
|
+
else
|
270
|
+
@objs[id] = true
|
271
|
+
@result.bytes += OBJ_OVERHEAD
|
272
|
+
@result.objects += 1
|
273
|
+
yield obj if block_given?
|
274
|
+
true
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|