Class: CloudFiles::Connection

Inherits:
Object
  • Object
show all
Defined in:
lib/cloudfiles/connection.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Connection

Creates a new CloudFiles::Connection object. Uses CloudFiles::Authentication to perform the login for the connection. The authuser is the Rackspace Cloud username, the authkey is the Rackspace Cloud API key.

Setting the optional retry_auth variable to false will cause an exception to be thrown if your authorization token expires. Otherwise, it will attempt to reauthenticate.

Setting the optional snet variable to true or setting an environment variable of RACKSPACE_SERVICENET to any value will cause storage URLs to be returned with a prefix pointing them to the internal Rackspace service network, instead of a public URL.

This is useful if you are using the library on a Rackspace-hosted system, as it provides faster speeds, keeps traffic off of the public network, and the bandwidth is not billed.

This will likely be the base class for most operations.

With gem 1.4.8, the connection style has changed. It is now a hash of arguments. Note that the proxy options are currently only supported in the new style.

cf = CloudFiles::Connection.new(:username => "MY_USERNAME", :api_key => "MY_API_KEY", :retry_auth => true, :snet => false, :proxy_host => "localhost", :proxy_port => "1234")

The old style (positional arguments) is deprecated and will be removed at some point in the future.

cf = CloudFiles::Connection.new(MY_USERNAME, MY_API_KEY, RETRY_AUTH, USE_SNET)


74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/cloudfiles/connection.rb', line 74

def initialize(*args)
  if args[0].is_a?(Hash)
    options = args[0]
    @authuser = options[:username] ||( raise AuthenticationException, "Must supply a :username")
    @authkey = options[:api_key] || (raise AuthenticationException, "Must supply an :api_key")
    @retry_auth = options[:retry_auth] || true
    @snet = ENV['RACKSPACE_SERVICENET'] || options[:snet]
    @proxy_host = options[:proxy_host]
    @proxy_port = options[:proxy_port]
  elsif args[0].is_a?(String)
    @authuser = args[0] ||( raise AuthenticationException, "Must supply the username as the first argument")
    @authkey = args[1] || (raise AuthenticationException, "Must supply the API key as the second argument")
    @retry_auth = args[2] || true
    @snet = (ENV['RACKSPACE_SERVICENET'] || args[3]) ? true : false
  end
  @authok = false
  @http = {}
  CloudFiles::Authentication.new(self)
end

Instance Attribute Details

#authkeyObject (readonly)

Authentication key provided when the CloudFiles class was instantiated



7
8
9
# File 'lib/cloudfiles/connection.rb', line 7

def authkey
  @authkey
end

#authokObject

Instance variable that is set when authorization succeeds



40
41
42
# File 'lib/cloudfiles/connection.rb', line 40

def authok
  @authok
end

#authtokenObject

Token returned after a successful authentication



10
11
12
# File 'lib/cloudfiles/connection.rb', line 10

def authtoken
  @authtoken
end

#authuserObject (readonly)

Authentication username provided when the CloudFiles class was instantiated



13
14
15
# File 'lib/cloudfiles/connection.rb', line 13

def authuser
  @authuser
end

#bytesObject (readonly)

The total size in bytes under this connection



43
44
45
# File 'lib/cloudfiles/connection.rb', line 43

def bytes
  @bytes
end

#cdnmgmthostObject

Hostname of the CDN management server



16
17
18
# File 'lib/cloudfiles/connection.rb', line 16

def cdnmgmthost
  @cdnmgmthost
end

#cdnmgmtpathObject

Path for managing containers on the CDN management server



19
20
21
# File 'lib/cloudfiles/connection.rb', line 19

def cdnmgmtpath
  @cdnmgmtpath
end

#cdnmgmtportObject

Port number for the CDN server



22
23
24
# File 'lib/cloudfiles/connection.rb', line 22

def cdnmgmtport
  @cdnmgmtport
end

#cdnmgmtschemeObject

URI scheme for the CDN server



25
26
27
# File 'lib/cloudfiles/connection.rb', line 25

def cdnmgmtscheme
  @cdnmgmtscheme
end

#countObject (readonly)

The total number of containers under this connection



46
47
48
# File 'lib/cloudfiles/connection.rb', line 46

def count
  @count
end

#proxy_hostObject (readonly)

Optional proxy variables



49
50
51
# File 'lib/cloudfiles/connection.rb', line 49

def proxy_host
  @proxy_host
end

#proxy_portObject (readonly)

Returns the value of attribute proxy_port.



50
51
52
# File 'lib/cloudfiles/connection.rb', line 50

def proxy_port
  @proxy_port
end

#storagehostObject

Hostname of the storage server



28
29
30
# File 'lib/cloudfiles/connection.rb', line 28

def storagehost
  @storagehost
end

#storagepathObject

Path for managing containers/objects on the storage server



31
32
33
# File 'lib/cloudfiles/connection.rb', line 31

def storagepath
  @storagepath
end

#storageportObject

Port for managing the storage server



34
35
36
# File 'lib/cloudfiles/connection.rb', line 34

def storageport
  @storageport
end

#storageschemeObject

URI scheme for the storage server



37
38
39
# File 'lib/cloudfiles/connection.rb', line 37

def storagescheme
  @storagescheme
end

Instance Method Details

#authok?Boolean

Returns true if the authentication was successful and returns false otherwise.

cf.authok?
=> true

Returns:

  • (Boolean)


98
99
100
# File 'lib/cloudfiles/connection.rb', line 98

def authok?
  @authok
end

#cfreq(method, server, path, port, scheme, headers = {}, data = nil, attempts = 0, &block) ⇒ Object

This method actually makes the HTTP calls out to the server



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/cloudfiles/connection.rb', line 253

def cfreq(method,server,path,port,scheme,headers = {},data = nil,attempts = 0,&block) # :nodoc:
  start = Time.now
  headers['Transfer-Encoding'] = "chunked" if data.is_a?(IO)
  hdrhash = headerprep(headers)
  start_http(server,path,port,scheme,hdrhash)
  request = Net::HTTP.const_get(method.to_s.capitalize).new(path,hdrhash)
  if data
    if data.respond_to?(:read)
      request.body_stream = data
    else
      request.body = data
    end
    unless data.is_a?(IO)
      request.content_length = data.respond_to?(:lstat) ? data.stat.size : data.size
    end
  else
    request.content_length = 0
  end
  response = @http[server].request(request,&block)
  raise ExpiredAuthTokenException if response.code == "401"
  response
rescue Errno::EPIPE, Timeout::Error, Errno::EINVAL, EOFError
  # Server closed the connection, retry
  raise ConnectionException, "Unable to reconnect to #{server} after #{count} attempts" if attempts >= 5
  attempts += 1
  @http[server].finish
  start_http(server,path,port,scheme,headers)
  retry
rescue ExpiredAuthTokenException
  raise ConnectionException, "Authentication token expired and you have requested not to retry" if @retry_auth == false
  CloudFiles::Authentication.new(self)
  retry
end

#container(name) ⇒ Object Also known as: get_container

Returns an CloudFiles::Container object that can be manipulated easily. Throws a NoSuchContainerException if the container doesn’t exist.

container = cf.container('test')
container.count
=> 2


113
114
115
# File 'lib/cloudfiles/connection.rb', line 113

def container(name)
  CloudFiles::Container.new(self,name)
end

#container_exists?(containername) ⇒ Boolean

Returns true if the requested container exists and returns false otherwise.

cf.container_exists?('good_container')
=> true

cf.container_exists?('bad_container')
=> false

Returns:

  • (Boolean)


192
193
194
195
# File 'lib/cloudfiles/connection.rb', line 192

def container_exists?(containername)
  response = cfreq("HEAD",@storagehost,"#{@storagepath}/#{URI.encode(containername).gsub(/&/,'%26')}",@storageport,@storagescheme)
  return (response.code == "204")? true : false ;
end

#containers(limit = 0, marker = "") ⇒ Object Also known as: list_containers

Gathers a list of the containers that exist for the account and returns the list of container names as an array. If no containers exist, an empty array is returned. Throws an InvalidResponseException if the request fails.

If you supply the optional limit and marker parameters, the call will return the number of containers specified in limit, starting after the object named in marker.

cf.containers
=> ["backup", "Books", "cftest", "test", "video", "webpics"] 

cf.containers(2,'cftest')
=> ["test", "video"]


145
146
147
148
149
150
151
152
153
154
# File 'lib/cloudfiles/connection.rb', line 145

def containers(limit=0,marker="")
  paramarr = []
  paramarr << ["limit=#{URI.encode(limit.to_s).gsub(/&/,'%26')}"] if limit.to_i > 0
  paramarr << ["offset=#{URI.encode(marker.to_s).gsub(/&/,'%26')}"] unless marker.to_s.empty?
  paramstr = (paramarr.size > 0)? paramarr.join("&") : "" ;
  response = cfreq("GET",@storagehost,"#{@storagepath}?#{paramstr}",@storageport,@storagescheme)
  return [] if (response.code == "204")
  raise InvalidResponseException, "Invalid response code #{response.code}" unless (response.code == "200")
  CloudFiles.lines(response.body)
end

#containers_detail(limit = 0, marker = "") ⇒ Object Also known as: list_containers_info

Retrieves a list of containers on the account along with their sizes (in bytes) and counts of the objects held within them. If no containers exist, an empty hash is returned. Throws an InvalidResponseException if the request fails.

If you supply the optional limit and marker parameters, the call will return the number of containers specified in limit, starting after the object named in marker.

cf.containers_detail              
=> { "container1" => { :bytes => "36543", :count => "146" }, 
     "container2" => { :bytes => "105943", :count => "25" } }


167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/cloudfiles/connection.rb', line 167

def containers_detail(limit=0,marker="")
  paramarr = []
  paramarr << ["limit=#{URI.encode(limit.to_s).gsub(/&/,'%26')}"] if limit.to_i > 0
  paramarr << ["offset=#{URI.encode(marker.to_s).gsub(/&/,'%26')}"] unless marker.to_s.empty?
  paramstr = (paramarr.size > 0)? paramarr.join("&") : "" ;
  response = cfreq("GET",@storagehost,"#{@storagepath}?format=xml&#{paramstr}",@storageport,@storagescheme)
  return {} if (response.code == "204")
  raise InvalidResponseException, "Invalid response code #{response.code}" unless (response.code == "200")
  doc = REXML::Document.new(response.body)
  detailhash = {}
  doc.elements.each("account/container/") { |c|
    detailhash[c.elements["name"].text] = { :bytes => c.elements["bytes"].text, :count => c.elements["count"].text  }
  }
  doc = nil
  return detailhash
end

#create_container(containername) ⇒ Object

Creates a new container and returns the CloudFiles::Container object. Throws an InvalidResponseException if the request fails.

Slash (/) and question mark (?) are invalid characters, and will be stripped out. The container name is limited to 256 characters or less.

container = cf.create_container('new_container')
container.name
=> "new_container"

container = cf.create_container('bad/name')
=> SyntaxException: Container name cannot contain the characters '/' or '?'

Raises:



209
210
211
212
213
214
215
# File 'lib/cloudfiles/connection.rb', line 209

def create_container(containername)
  raise SyntaxException, "Container name cannot contain the characters '/' or '?'" if containername.match(/[\/\?]/)
  raise SyntaxException, "Container name is limited to 256 characters" if containername.length > 256
  response = cfreq("PUT",@storagehost,"#{@storagepath}/#{URI.encode(containername).gsub(/&/,'%26')}",@storageport,@storagescheme)
  raise InvalidResponseException, "Unable to create container #{containername}" unless (response.code == "201" || response.code == "202")
  CloudFiles::Container.new(self,containername)
end

#delete_container(containername) ⇒ Object

Deletes a container from the account. Throws a NonEmptyContainerException if the container still contains objects. Throws a NoSuchContainerException if the container doesn’t exist.

cf.delete_container('new_container')
=> true

cf.delete_container('video')
=> NonEmptyContainerException: Container video is not empty

cf.delete_container('nonexistent')
=> NoSuchContainerException: Container nonexistent does not exist


228
229
230
231
232
233
# File 'lib/cloudfiles/connection.rb', line 228

def delete_container(containername)
  response = cfreq("DELETE",@storagehost,"#{@storagepath}/#{URI.encode(containername).gsub(/&/,'%26')}",@storageport,@storagescheme)
  raise NonEmptyContainerException, "Container #{containername} is not empty" if (response.code == "409")
  raise NoSuchContainerException, "Container #{containername} does not exist" unless (response.code == "204")
  true
end

#get_infoObject

Sets instance variables for the bytes of storage used for this account/connection, as well as the number of containers stored under the account. Returns a hash with :bytes and :count keys, and also sets the instance variables.

cf.get_info
=> {:count=>8, :bytes=>42438527}
cf.bytes
=> 42438527


125
126
127
128
129
130
131
# File 'lib/cloudfiles/connection.rb', line 125

def get_info
  response = cfreq("HEAD",@storagehost,@storagepath,@storageport,@storagescheme)
  raise InvalidResponseException, "Unable to obtain account size" unless (response.code == "204")
  @bytes = response["x-account-bytes-used"].to_i
  @count = response["x-account-container-count"].to_i
  {:bytes => @bytes, :count => @count}
end

#public_containers(enabled_only = false) ⇒ Object

Gathers a list of public (CDN-enabled) containers that exist for an account and returns the list of container names as an array. If no containers are public, an empty array is returned. Throws a InvalidResponseException if the request fails.

If you pass the optional argument as true, it will only show containers that are CURRENTLY being shared on the CDN, as opposed to the default behavior which is to show all containers that have EVER been public.

cf.public_containers
=> ["video", "webpics"]


244
245
246
247
248
249
250
# File 'lib/cloudfiles/connection.rb', line 244

def public_containers(enabled_only = false)
  paramstr = enabled_only == true ? "enabled_only=true" : ""
  response = cfreq("GET",@cdnmgmthost,"#{@cdnmgmtpath}?#{paramstr}",@cdnmgmtport,@cdnmgmtscheme)
  return [] if (response.code == "204")
  raise InvalidResponseException, "Invalid response code #{response.code}" unless (response.code == "200")
  CloudFiles.lines(response.body)
end

#snet?Boolean

Returns true if the library is requesting the use of the Rackspace service network

Returns:

  • (Boolean)


103
104
105
# File 'lib/cloudfiles/connection.rb', line 103

def snet?
  @snet
end