Class: Raca::Container

Inherits:
Object
  • Object
show all
Defined in:
lib/raca/container.rb

Overview

Represents a single cloud files container. Contains methods for uploading, downloading, collecting stats, listing files, etc.

You probably don’t want to instantiate this directly, see Raca::Account#containers

Constant Summary collapse

MAX_ITEMS_PER_LIST =
10_000
LARGE_FILE_THRESHOLD =

5 Gb

5_368_709_120
LARGE_FILE_SEGMENT_SIZE =

4 Gb

4_295_000_000

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(account, region, container_name, opts = {}) ⇒ Container

Returns a new instance of Container.

Raises:

  • (ArgumentError)


21
22
23
24
25
26
27
28
# File 'lib/raca/container.rb', line 21

def initialize(, region, container_name, opts = {})
  raise ArgumentError, "The container name must not contain '/'." if container_name['/']
  @account, @region, @container_name = , region, container_name
  @storage_url = @account.public_endpoint("cloudFiles", region)
  @cdn_url     = @account.public_endpoint("cloudFilesCDN", region)
  @logger = opts[:logger]
  @logger ||= Rails.logger if defined?(Rails)
end

Instance Attribute Details

#container_nameObject (readonly)

Returns the value of attribute container_name.



19
20
21
# File 'lib/raca/container.rb', line 19

def container_name
  @container_name
end

Instance Method Details

#cdn_enable(ttl = 259200) ⇒ Object

use this with caution, it will make EVERY object in the container publicly available via the CDN. CDN enabling can be done via the web UI but only with a TTL of 72 hours. Using the API it’s possible to set a TTL of 50 years.

TTL is defined in seconds, default is 72 hours.



204
205
206
207
208
209
# File 'lib/raca/container.rb', line 204

def cdn_enable(ttl = 259200)
  log "enabling CDN access to #{container_path} with a cache expiry of #{ttl / 60} minutes"

  response = cdn_client.put(container_path, "X-TTL" => ttl.to_i.to_s)
  (200..299).cover?(response.code.to_i)
end

#cdn_metadataObject

Return the key details for CDN access to this container. Can be called on non CDN enabled containers, but the details won’t make much sense.



185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/raca/container.rb', line 185

def 
  log "retrieving container CDN metadata from #{container_path}"
  response = cdn_client.head(container_path)
  {
    :cdn_enabled => response["X-CDN-Enabled"] == "True",
    :host => response["X-CDN-URI"],
    :ssl_host => response["X-CDN-SSL-URI"],
    :streaming_host => response["X-CDN-STREAMING-URI"],
    :ttl => response["X-TTL"].to_i,
    :log_retention => response["X-Log-Retention"] == "True"
  }
end

#delete(key) ⇒ Object

Delete key from the container. If the container is on the CDN, the object will still be served from the CDN until the TTL expires.



50
51
52
53
54
55
# File 'lib/raca/container.rb', line 50

def delete(key)
  log "deleting #{key} from #{container_path}"
  object_path = File.join(container_path, Raca::Util.url_encode(key))
  response = storage_client.delete(object_path)
  (200..299).cover?(response.code.to_i)
end

#download(key, filepath) ⇒ Object

Download the object at key into a local file at filepath.

Returns the number of downloaded bytes.



90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/raca/container.rb', line 90

def download(key, filepath)
  log "downloading #{key} from #{container_path}"
  object_path = File.join(container_path, Raca::Util.url_encode(key))
  outer_response = storage_client.get(object_path) do |response|
    File.open(filepath, 'wb') do |io|
      response.read_body do |chunk|
        io.write(chunk)
      end
    end
  end
  outer_response["Content-Length"].to_i
end

#expiring_url(object_key, temp_url_key, expires_at = Time.now.to_i + 60) ⇒ Object

DEPRECATED: use temp_url instead, this will be removed in version 1.0



220
221
222
# File 'lib/raca/container.rb', line 220

def expiring_url(object_key, temp_url_key, expires_at = Time.now.to_i + 60)
  temp_url(object_key, temp_url_key, expires_at)
end

#inspectObject



232
233
234
# File 'lib/raca/container.rb', line 232

def inspect
  "#<Raca::Container:#{__id__} region=#{@region} container_name=#{@container_name}>"
end

#list(options = {}) ⇒ Object

Return an array of files in the container.

Supported options

max - the maximum number of items to return marker - return items alphabetically after this key. Useful for pagination prefix - only return items that start with this string details - return extra details for each file - size, md5, etc



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/raca/container.rb', line 112

def list(options = {})
  max = options.fetch(:max, 100_000_000)
  marker = options.fetch(:marker, nil)
  prefix = options.fetch(:prefix, nil)
  details = options.fetch(:details, nil)
  limit = [max, MAX_ITEMS_PER_LIST].min
  log "retrieving up to #{max} items from #{container_path}"
  request_path = list_request_path(marker, prefix, details, limit)
  result = storage_client.get(request_path).body || ""
  if details
    result = JSON.parse(result)
  else
    result = result.split("\n")
  end
  result.tap {|items|
    if max <= limit
      log "Got #{items.length} items; we don't need any more."
    elsif items.length < limit
      log "Got #{items.length} items; there can't be any more."
    else
      log "Got #{items.length} items; requesting #{limit} more."
      details ? marker = items.last["name"] : marker = items.last
      items.concat list(max: max-items.length, marker: marker, prefix: prefix, details: details)
    end
  }
end

#metadataObject

Return some basic stats on the current container.



151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/raca/container.rb', line 151

def 
  log "retrieving container metadata from #{container_path}"
  response = storage_client.head(container_path)
  custom = {}
  response.each_capitalized_name { |name|
    custom[name] = response[name] if name[/\AX-Container-Meta-/]
  }
  {
    :objects => response["X-Container-Object-Count"].to_i,
    :bytes => response["X-Container-Bytes-Used"].to_i,
    :custom => custom,
  }
end

#object_metadata(key) ⇒ Object

Returns some metadata about a single object in this container.



75
76
77
78
79
80
81
82
83
84
# File 'lib/raca/container.rb', line 75

def (key)
  object_path = File.join(container_path, Raca::Util.url_encode(key))
  log "Requesting metadata from #{object_path}"

  response = storage_client.head(object_path)
  {
    :content_type => response["Content-Type"],
    :bytes => response["Content-Length"].to_i
  }
end

#purge_from_akamai(key, email_address) ⇒ Object

Remove key from the CDN edge nodes on which it is currently cached. The object is not deleted from the container: as the URL is re-requested, the edge cache will be re-filled with the object currently in the container.

This shouldn’t be used except when it’s really required (e.g. when a piece has to be taken down) because it’s expensive: it lodges a support ticket at Akamai. (!)



64
65
66
67
68
69
70
71
# File 'lib/raca/container.rb', line 64

def purge_from_akamai(key, email_address)
  log "Requesting #{File.join(container_path, key)} to be purged from the CDN"
  response = cdn_client.delete(
    File.join(container_path, Raca::Util.url_encode(key)),
    'X-Purge-Email' => email_address
  )
  (200..299).cover?(response.code.to_i)
end

#search(prefix) ⇒ Object

Returns an array of object keys that start with prefix. This is a convenience method that is equivilant to:

container.list(prefix: "foo/bar/")


144
145
146
147
# File 'lib/raca/container.rb', line 144

def search(prefix)
  log "retrieving container listing from #{container_path} items starting with #{prefix}"
  list(prefix: prefix)
end

#set_metadata(headers) ⇒ Object

Set metadata headers on the container

headers = { "X-Container-Meta-Access-Control-Allow-Origin" => "*" }
container.(headers)

Note: Rackspace requires some headers to begin with ‘X-Container-Meta-’ or other prefixes, e.g. when setting

'Access-Control-Allow-Origin', it needs to be set as 'X-Container-Meta-Access-Control-Allow-Origin'.

See: docs.rackspace.com/files/api/v1/cf-devguide/content/CORS_Container_Header-d1e1300.html

http://docs.rackspace.com/files/api/v1/cf-devguide/content/
  POST_updateacontainermeta_v1__account___container__containerServicesOperations_d1e000.html


176
177
178
179
180
# File 'lib/raca/container.rb', line 176

def (headers)
  log "setting headers for container #{container_path}"
  response = storage_client.post(container_path, '', headers)
  (200..299).cover?(response.code.to_i)
end

#temp_upload_url(object_key, temp_url_key, expires_at = Time.now.to_i + 60) ⇒ Object

Generate a temporary URL for uploading a file to a private container. Anyone can perform a PUT request to the URL returned from this method and an object will be created in the container.



228
229
230
# File 'lib/raca/container.rb', line 228

def temp_upload_url(object_key, temp_url_key, expires_at = Time.now.to_i + 60)
  private_url("PUT", object_key, temp_url_key, expires_at)
end

#temp_url(object_key, temp_url_key, expires_at = Time.now.to_i + 60) ⇒ Object

Generate an expiring URL for downloading a file that is otherwise private. Useful for providing temporary access to files.



214
215
216
# File 'lib/raca/container.rb', line 214

def temp_url(object_key, temp_url_key, expires_at = Time.now.to_i + 60)
  private_url("GET", object_key, temp_url_key, expires_at)
end

#upload(key, data_or_path, headers = {}) ⇒ Object

Upload data_or_path (which may be a filename or an IO) to the container, as key.

If headers are provided they will be added to to upload request. Use this to manually specify content type, content disposition, CORS headers, etc.



35
36
37
38
39
40
41
42
43
44
45
# File 'lib/raca/container.rb', line 35

def upload(key, data_or_path, headers = {})
  if data_or_path.respond_to?(:read) && data_or_path.respond_to?(:size)
    upload_io(key, data_or_path, data_or_path.size, headers)
  elsif !File.file?(data_or_path.to_s)
    raise ArgumentError, "data_or_path must be an IO with data or filename string"
  else
    File.open(data_or_path.to_s, "rb") do |io|
      upload_io(key, io, io.stat.size, headers)
    end
  end
end