Class: Fog::Storage::Softlayer::Real

Inherits:
Object
  • Object
show all
Includes:
Integrity
Defined in:
lib/fog/softlayer/storage.rb,
lib/fog/softlayer/requests/storage/get_object.rb,
lib/fog/softlayer/requests/storage/put_object.rb,
lib/fog/softlayer/requests/storage/copy_object.rb,
lib/fog/softlayer/requests/storage/head_object.rb,
lib/fog/softlayer/requests/storage/delete_object.rb,
lib/fog/softlayer/requests/storage/get_container.rb,
lib/fog/softlayer/requests/storage/put_container.rb,
lib/fog/softlayer/requests/storage/get_containers.rb,
lib/fog/softlayer/requests/storage/head_container.rb,
lib/fog/softlayer/requests/storage/head_containers.rb,
lib/fog/softlayer/requests/storage/delete_container.rb,
lib/fog/softlayer/requests/storage/put_object_manifest.rb,
lib/fog/softlayer/requests/storage/get_object_https_url.rb,
lib/fog/softlayer/requests/storage/delete_multiple_objects.rb,
lib/fog/softlayer/requests/storage/put_static_obj_manifest.rb,
lib/fog/softlayer/requests/storage/put_dynamic_obj_manifest.rb,
lib/fog/softlayer/requests/storage/delete_static_large_object.rb,
lib/fog/softlayer/requests/storage/post_set_meta_temp_url_key.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Integrity

#validate_username!

Constructor Details

#initialize(options = {}) ⇒ Real

Returns a new instance of Real.



95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/fog/softlayer/storage.rb', line 95

def initialize(options={})
  @api_key = options[:softlayer_api_key]
  @username = options[:softlayer_username]
  validate_username! @username
  @cluster = options[:softlayer_cluster]
  @storage_account = options[:softlayer_storage_account] || (options[:softlayer_username], options[:softlayer_api_key])
  @connection_options     = options[:connection_options] || {}
  @bluemix_objstor_auth_url = options[:softlayer_bluemix_objstor_auth_url] || nil
  authenticate
  @persistent = options[:persistent] || false
  @connection = Fog::Core::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent, @connection_options)
  @temp_url_key = options[:softlayer_temp_url_key] || 
end

Instance Attribute Details

#auth_expiresObject

Returns the value of attribute auth_expires.



93
94
95
# File 'lib/fog/softlayer/storage.rb', line 93

def auth_expires
  @auth_expires
end

#auth_tokenObject

Returns the value of attribute auth_token.



93
94
95
# File 'lib/fog/softlayer/storage.rb', line 93

def auth_token
  @auth_token
end

#auth_urlObject (readonly)

Returns the value of attribute auth_url.



92
93
94
# File 'lib/fog/softlayer/storage.rb', line 92

def auth_url
  @auth_url
end

#clusterObject (readonly)

Returns the value of attribute cluster.



92
93
94
# File 'lib/fog/softlayer/storage.rb', line 92

def cluster
  @cluster
end

Instance Method Details

#copy_object(source_container, source_object, target_container, target_object, options = {}) ⇒ Object

Copy object

Parameters

  • source_container_name<~String> - Name of source bucket

  • source_object_name<~String> - Name of source object

  • target_container_name<~String> - Name of bucket to create copy in

  • target_object_name<~String> - Name for new copy of object

  • options<~Hash> - Additional headers



29
30
31
32
33
34
35
36
37
# File 'lib/fog/softlayer/requests/storage/copy_object.rb', line 29

def copy_object(source_container, source_object, target_container, target_object, options={})
  headers = { 'X-Copy-From' => "/#{source_container}/#{source_object}" }.merge(options)
  request({
    :expects  => 201,
    :headers  => headers,
    :method   => 'PUT',
    :path     => "#{Fog::Softlayer.escape(target_container)}/#{Fog::Softlayer.escape(target_object)}"
  })
end

#create_temp_url(container, object, expires, method, options = {}) ⇒ Object

creates a temporary url

Parameters

  • container<~String> - Name of container containing object

  • object<~String> - Name of object to get expiring url for

  • expires<~Time> - An expiry time for this url

  • method<~String> - The method to use for accessing the object (GET, PUT, HEAD)

  • scheme<~String> - The scheme to use (http, https)

  • options<~Hash> - An optional options hash

Returns

  • response<~Excon::Response>:

    • body<~String> - url for object

Raises:

  • (ArgumentError)


40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/fog/softlayer/requests/storage/get_object_https_url.rb', line 40

def  create_temp_url(container, object, expires, method, options = {})
  raise ArgumentError, "Insufficient parameters specified." unless (container && object && expires && method)
  raise ArgumentError, "Storage must be instantiated with the :temp_url_key option" if @temp_url_key.nil?

  scheme = options[:scheme] || @scheme

  # POST not allowed
  allowed_methods = %w{GET PUT HEAD}
  unless allowed_methods.include?(method)
    raise ArgumentError.new("Invalid method '#{method}' specified. Valid methods are: #{allowed_methods.join(', ')}")
  end

  expires        = expires.to_i
  object_path_escaped   = "#{@path}/#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object,"/")}"
  object_path_unescaped = "#{@path}/#{Fog::Softlayer.escape(container)}/#{object}"
  string_to_sign = "#{method}\n#{expires}\n#{object_path_unescaped}"

  hmac = Fog::HMAC.new('sha1', @temp_url_key)
  sig  = sig_to_hex(hmac.sign(string_to_sign))

  temp_url_options = {
    :scheme => scheme,
    :host => @host,
    :port => @port,
    :path => object_path_escaped,
    :query => URI.encode_www_form(
      :temp_url_sig => sig,
      :temp_url_expires => expires
    )
  }

  URI::Generic.build(temp_url_options).to_s
end

#delete_container(name) ⇒ Object

Delete an existing container

Parameters

  • name<~String> - Name of container to delete



27
28
29
30
31
32
33
# File 'lib/fog/softlayer/requests/storage/delete_container.rb', line 27

def delete_container(name)
  request(
    :expects  => 204,
    :method   => 'DELETE',
    :path     => Fog::Softlayer.escape(name)
  )
end

#delete_multiple_objects(container, object_names, options = {}) ⇒ Excon::Response

Deletes multiple objects or containers with a single request.

To delete objects from a single container, container may be provided and object_names should be an Array of object names within the container.

To delete objects from multiple containers or delete containers, container should be nil and all object_names should be prefixed with a container name.

Containers must be empty when deleted. object_names are processed in the order given, so objects within a container should be listed first to empty the container.

Up to 10,000 objects may be deleted in a single request. The server will respond with 200 OK for all requests. response.body must be inspected for actual results.

Examples:

Delete objects from a container

object_names = ['object', 'another/object']
conn.delete_multiple_objects('my_container', object_names)

Delete objects from multiple containers

object_names = ['container_a/object', 'container_b/object']
conn.delete_multiple_objects(nil, object_names)

Delete a container and all it’s objects

object_names = ['my_container/object_a', 'my_container/object_b', 'my_container']
conn.delete_multiple_objects(nil, object_names)

Parameters:

  • container (String, nil)

    Name of container.

  • object_names (Array<String>)

    Object names to be deleted.

  • options (Hash) (defaults to: {})

    Additional request headers.

Returns:

  • (Excon::Response)
    • body [Hash] - Results of the operation.

      • “Number Not Found” [Integer] - Number of missing objects or containers.

      • “Response Status” [String] - Response code for the subrequest of the last failed operation.

      • “Errors” [Array<object_name, response_status>]

        • object_name [String] - Object that generated an error when the delete was attempted.

        • response_status [String] - Response status from the subrequest for object_name.

      • “Number Deleted” [Integer] - Number of objects or containers deleted.

      • “Response Body” [String] - Response body for “Response Status”.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/fog/softlayer/requests/storage/delete_multiple_objects.rb', line 46

def delete_multiple_objects(container, object_names, options = {})
  body = object_names.map do |name|
    object_name = container ? "#{ container }/#{ name }" : name
    URI.encode(object_name)
  end.join("\n")

  response = request({
    :expects  => 200,
    :method   => 'DELETE',
    :headers  => options.merge('Content-Type' => 'text/plain',
                               'Accept' => 'application/json'),
    :body     => body,
    :query    => { 'bulk-delete' => true }
  }, false)
  response.body = Fog::JSON.decode(response.body)
  response
end

#delete_object(container, object) ⇒ Object

Delete an existing object

Parameters

  • container<~String> - Name of container to delete

  • object<~String> - Name of object to delete



29
30
31
32
33
34
35
# File 'lib/fog/softlayer/requests/storage/delete_object.rb', line 29

def delete_object(container, object)
  request(
    :expects  => 204,
    :method   => 'DELETE',
    :path     => "#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object)}"
  )
end

#delete_static_large_object(container, object, options = {}) ⇒ Excon::Response

Delete a static large object.

Deletes the SLO manifest object and all segments that it references. The server will respond with 200 OK for all requests. response.body must be inspected for actual results.

Parameters:

  • container (String)

    Name of container.

  • object (String)

    Name of the SLO manifest object.

  • options (Hash) (defaults to: {})

    Additional request headers.

Returns:

  • (Excon::Response)
    • body [Hash] - Results of the operation.

      • “Number Not Found” [Integer] - Number of missing segments.

      • “Response Status” [String] - Response code for the subrequest of the last failed operation.

      • “Errors” [Array<object_name, response_status>]

        • object_name [String] - Object that generated an error when the delete was attempted.

        • response_status [String] - Response status from the subrequest for object_name.

      • “Number Deleted” [Integer] - Number of segments deleted.

      • “Response Body” [String] - Response body for Response Status.

See Also:



27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/fog/softlayer/requests/storage/delete_static_large_object.rb', line 27

def delete_static_large_object(container, object, options = {})
  response = request({
    :expects  => 200,
    :method   => 'DELETE',
    :headers  => options.merge('Content-Type' => 'text/plain',
                               'Accept' => 'application/json'),
    :path     => "#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object)}",
    :query    => { 'multipart-manifest' => 'delete' }
  }, false)
  response.body = Fog::JSON.decode(response.body)
  response
end

#get_container(container, options = {}) ⇒ Object

Get details for container and total bytes stored

Parameters

  • container<~String> - Name of container to retrieve info for

  • options<~String>:

    • ‘limit’<~String> - Maximum number of objects to return

    • ‘marker’<~String> - Only return objects whose name is greater than marker

    • ‘prefix’<~String> - Limits results to those starting with prefix

    • ‘path’<~String> - Return objects nested in the pseudo path

Returns

  • response<~Excon::Response>:

    • headers<~Hash>:

      • ‘X-Account-Container-Count’<~String> - Count of containers

      • ‘X-Account-Bytes-Used’<~String> - Bytes used

    • body<~Array>:

      • ‘bytes’<~Integer> - Number of bytes used by container

      • ‘count’<~Integer> - Number of items in container

      • ‘name’<~String> - Name of container

      • item<~Hash>:

        • ‘bytes’<~String> - Size of object

        • ‘content_type’<~String> Content-Type of object

        • ‘hash’<~String> - Hash of object (etag?)

        • ‘last_modified’<~String> - Last modified timestamp

        • ‘name’<~String> - Name of object



55
56
57
58
59
60
61
62
63
# File 'lib/fog/softlayer/requests/storage/get_container.rb', line 55

def get_container(container, options = {})
  options = options.reject {|key, value| value.nil?}
  request(
    :expects  => 200,
    :method   => 'GET',
    :path     => Fog::Softlayer.escape(container),
    :query    => {'format' => 'json'}.merge!(options)
  )
end

#get_containers(options = {}) ⇒ Object

List existing storage containers

Parameters

  • options<~Hash>:

    • ‘limit’<~Integer> - Upper limit to number of results returned

    • ‘marker’<~String> - Only return objects with name greater than this value

Returns

  • response<~Excon::Response>:

    • body<~Array>:

      • container<~Hash>:

        • ‘bytes’<~Integer>: - Number of bytes used by container

        • ‘count’<~Integer>: - Number of items in container

        • ‘name’<~String>: - Name of container



38
39
40
41
42
43
44
45
46
# File 'lib/fog/softlayer/requests/storage/get_containers.rb', line 38

def get_containers(options = {})
  options = options.reject {|key, value| value.nil?}
  request(
    :expects  => [200, 204],
    :method   => 'GET',
    :path     => '',
    :query    => {'format' => 'json'}.merge!(options)
  )
end

#get_object(container, object, &block) ⇒ Object

Get details for object

Parameters

  • container<~String> - Name of container to look in

  • object<~String> - Name of object to look for



28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/fog/softlayer/requests/storage/get_object.rb', line 28

def get_object(container, object, &block)
  params = {
    :expects  => 200,
    :method   => 'GET',
    :path     => "#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object)}"
  }

  if block_given?
    params[:response_block] = block
  end

  request(params, false)
end

#get_object_https_url(container, object, expires, options = {}) ⇒ Object

Get an expiring object https url from Cloud Files

Parameters

  • container<~String> - Name of container containing object

  • object<~String> - Name of object to get expiring url for

  • expires<~Time> - An expiry time for this url

Returns

  • response<~Excon::Response>:

    • body<~String> - url for object



22
23
24
# File 'lib/fog/softlayer/requests/storage/get_object_https_url.rb', line 22

def get_object_https_url(container, object, expires, options = {})
  create_temp_url(container, object, expires, "GET", options.merge(:scheme => "https"))
end

#head_container(container) ⇒ Object

List number of objects and total bytes stored

Parameters

  • container<~String> - Name of container to retrieve info for

Returns

  • response<~Excon::Response>:

    • headers<~Hash>:

      • ‘X-Container-Object-Count’<~String> - Count of containers

      • ‘X-Container-Bytes-Used’<~String> - Bytes used



16
17
18
19
20
21
22
23
# File 'lib/fog/softlayer/requests/storage/head_container.rb', line 16

def head_container(container)
  request(
    :expects  => 204,
    :method   => 'HEAD',
    :path     => Fog::Softlayer.escape(container),
    :query    => {'format' => 'json'}
  )
end

#head_containersObject

List number of containers and total bytes stored

Returns

  • response<~Excon::Response>:

    • headers<~Hash>:

      • ‘X-Account-Container-Count’<~String> - Count of containers

      • ‘X-Account-Bytes-Used’<~String> - Bytes used



13
14
15
16
17
18
19
20
# File 'lib/fog/softlayer/requests/storage/head_containers.rb', line 13

def head_containers
  request(
    :expects  => 204,
    :method   => 'HEAD',
    :path     => '',
    :query    => {'format' => 'json'}
  )
end

#head_object(container, object) ⇒ Object

Get headers for object

Parameters

  • container<~String> - Name of container to look in

  • object<~String> - Name of object to look for



12
13
14
15
16
17
18
# File 'lib/fog/softlayer/requests/storage/head_object.rb', line 12

def head_object(container, object)
  request({
    :expects  => 200,
    :method   => 'HEAD',
    :path     => "#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object)}"
  }, false)
end

#post_set_meta_temp_url_key(key) ⇒ Object

Set the account wide Temp URL Key. This is a secret key that’s used to generate signed expiring URLs.

Once the key has been set with this request you should create new Storage objects with the :temp_url_key option then use the get_object_https_url method to generate expiring URLs.

*** CAUTION *** changing this secret key will invalidate any expiring URLS generated with old keys.

Parameters

  • key<~String> - The new Temp URL Key

Returns

  • response<~Excon::Response>

See Also

docs.rackspace.com/files/api/v1/cf-devguide/content/Set_Account_Metadata-d1a4460.html



25
26
27
28
29
30
31
32
33
# File 'lib/fog/softlayer/requests/storage/post_set_meta_temp_url_key.rb', line 25

def post_set_meta_temp_url_key(key)
  response = request(
    :expects  => [201, 202, 204],
    :method   => 'POST',
    :headers  => {'X-Account-Meta-Temp-Url-Key' => key}
  )
  @temp_url_key = 
  response
end

#put_container(name, public = false) ⇒ Object

Create a new container

Parameters

  • name<~String> - Name for container, should be < 256 bytes and must not contain ‘/’



21
22
23
24
25
26
27
28
29
# File 'lib/fog/softlayer/requests/storage/put_container.rb', line 21

def put_container(name, public=false)
  opts = {
    :expects  => [201, 202],
    :method   => 'PUT',
    :path     => Fog::Softlayer.escape(name),
  }
  opts[:headers] = { 'X-Container-Read' => '.r:*' } if public
  request(opts)
end

#put_dynamic_obj_manifest(container, object, options = {}) ⇒ Object

Create a new dynamic large object manifest

Creates an object with a X-Object-Manifest header that specifies the common prefix (“<container>/<prefix>”) for all uploaded segments. Retrieving the manifest object streams all segments matching this prefix. Segments must sort in the order they should be concatenated. Note that any future objects stored in the container along with the segments that match the prefix will be included when retrieving the manifest object.

All segments must be stored in the same container, but may be in a different container than the manifest object. The default X-Object-Manifest header is set to “container/object”, but may be overridden in options to specify the prefix and/or the container where segments were stored. If overridden, names should be CGI escaped (excluding spaces) if needed (see Fog::Softlayer.escape).

Parameters:

  • container (String)

    Name for container where object will be stored. Should be < 256 bytes and must not contain ‘/’

  • object (String)

    Name for manifest object.

  • options (Hash) (defaults to: {})

    Config headers for object.

Options Hash (options):

  • 'X-Object-Manifest' (String) — default: "container/object"

    “<container>/<prefix>” for segment objects.

Raises:

  • (Fog::Storage::Softlayer::NotFound)

    HTTP 404

  • (Excon::Errors::BadRequest)

    HTTP 400

  • (Excon::Errors::Unauthorized)

    HTTP 401

  • (Excon::Errors::HTTPStatusError)

See Also:



29
30
31
32
33
34
35
36
37
38
# File 'lib/fog/softlayer/requests/storage/put_dynamic_obj_manifest.rb', line 29

def put_dynamic_obj_manifest(container, object, options = {})
  path = "#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object)}"
  headers = {'X-Object-Manifest' => path}.merge(options)
  request(
    :expects  => 201,
    :headers  => headers,
    :method   => 'PUT',
    :path     => path
  )
end

#put_object(container, object, data, options = {}, &block) ⇒ Object

Create a new object

When passed a block, it will make a chunked request, calling the block for chunks until it returns an empty string. In this case the data parameter is ignored.

Parameters

  • container<~String> - Name for container, should be < 256 bytes and must not contain ‘/’

  • object<~String> - Name for object

  • data<~String|File> - data to upload

  • options<~Hash> - config headers for object. Defaults to {}.

  • block<~Proc> - chunker



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/fog/softlayer/requests/storage/put_object.rb', line 38

def put_object(container, object, data, options = {}, &block)
  if block_given?
    params = { :request_block => block }
    headers = options
  else
    data = Fog::Storage.parse_data(data)
    headers = data[:headers].merge!(options)
    params = { :body => data[:body] }
  end

  params.merge!(
    :expects    => 201,
    :idempotent => !params[:request_block],
    :headers    => headers,
    :method     => 'PUT',
    :path       => "#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object)}"
  )

  request(params)
end

#put_object_manifest(container, object, options = {}) ⇒ Object

Create a new dynamic large object manifest

This is an alias for #put_dynamic_obj_manifest for backward compatibility.



9
10
11
# File 'lib/fog/softlayer/requests/storage/put_object_manifest.rb', line 9

def put_object_manifest(container, object, options = {})
  put_dynamic_obj_manifest(container, object, options)
end

#put_static_obj_manifest(container, object, segments, options = {}) ⇒ Object

Create a new static large object manifest.

A static large object is similar to a dynamic large object. Whereas a GET for a dynamic large object manifest will stream segments based on the manifest’s X-Object-Manifest object name prefix, a static large object manifest streams segments which are defined by the user within the manifest. Information about each segment is provided in segments as an Array of Hash objects, ordered in the sequence which the segments should be streamed.

When the SLO manifest is received, each segment’s etag and size_bytes will be verified. The etag for each segment is returned in the response to #put_object, but may also be calculated. e.g. Digest::MD5.hexdigest(segment_data)

The maximum number of segments for a static large object is 1000, and all segments (except the last) must be at least 1 MiB in size. Unlike a dynamic large object, segments are not required to be in the same container.

Examples:

segments = [
  { :path => 'segments_container/first_segment',
    :etag => 'md5 for first_segment',
    :size_bytes => 'byte size of first_segment' },
  { :path => 'segments_container/second_segment',
    :etag => 'md5 for second_segment',
    :size_bytes => 'byte size of second_segment' }
]
put_static_obj_manifest('my_container', 'my_large_object', segments)

Parameters:

  • container (String)

    Name for container where object will be stored. Should be < 256 bytes and must not contain ‘/’

  • object (String)

    Name for manifest object.

  • segments (Array<Hash>)

    Segment data for the object.

  • options (Hash) (defaults to: {})

    Config headers for object.

Raises:

  • (Fog::Storage::Softlayer::NotFound)

    HTTP 404

  • (Excon::Errors::BadRequest)

    HTTP 400

  • (Excon::Errors::Unauthorized)

    HTTP 401

  • (Excon::Errors::HTTPStatusError)

See Also:



43
44
45
46
47
48
49
50
51
52
# File 'lib/fog/softlayer/requests/storage/put_static_obj_manifest.rb', line 43

def put_static_obj_manifest(container, object, segments, options = {})
  request(
    :expects  => 201,
    :method   => 'PUT',
    :headers  => options,
    :body     => Fog::JSON.encode(segments),
    :path     => "#{Fog::Softlayer.escape(container)}/#{Fog::Softlayer.escape(object)}",
    :query    => { 'multipart-manifest' => 'put' }
  )
end

#reloadObject



113
114
115
# File 'lib/fog/softlayer/storage.rb', line 113

def reload
  @connection.reset
end

#request(params = {}, parse_json = true) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/fog/softlayer/storage.rb', line 117

def request(params = {}, parse_json = true)
  begin
    params.is_a?(Hash) or raise ArgumentError, "#{self.class}#request params must be a Hash"
    params = _build_params(params)
    response = @connection.request(params)

    if response.status == 401 && !!@auth_token
      @auth_token = nil; @auth_expires = nil
      authenticate
      response = @connection.request(params)
    end

    if !response.body.empty? && parse_json && response.get_header('Content-Type') =~ %r{application/json}
      response.body = Fog::JSON.decode(response.body)
    end

    response
  rescue Excon::Errors::HTTPStatusError => error
    raise case error
      when Excon::Errors::NotFound
        Fog::Storage::Softlayer::NotFound.slurp(error)
      else
        error
    end
  end
end