Skip to content

Commit

Permalink
Merge pull request #48 from tobowers/dev/connection-cleanup-and-acl-s…
Browse files Browse the repository at this point in the history
…upport

Dev/connection cleanup and acl support
  • Loading branch information
minter committed Aug 10, 2011
2 parents 0d913df + eacd5a1 commit ea9aa70
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 94 deletions.
24 changes: 17 additions & 7 deletions lib/cloudfiles/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def container(name)
# cf.bytes
# => 42438527
def get_info
response = cfreq("HEAD", @storagehost, @storagepath, @storageport, @storagescheme)
response = storage_request("HEAD")
raise CloudFiles::Exception::InvalidResponse, "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
Expand Down Expand Up @@ -166,7 +166,7 @@ def containers(limit = 0, marker = "")
query = []
query << "limit=#{CloudFiles.escape limit.to_s}" if limit.to_i > 0
query << "marker=#{CloudFiles.escape marker.to_s}" unless marker.to_s.empty?
response = cfreq("GET", @storagehost, "#{@storagepath}?#{query.join '&'}", @storageport, @storagescheme)
response = storage_request("GET", "?#{query.join('&')}")
return [] if (response.code == "204")
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "200")
CloudFiles.lines(response.body)
Expand All @@ -187,7 +187,7 @@ def containers_detail(limit = 0, marker = "")
query = ['format=xml']
query << "limit=#{CloudFiles.escape limit.to_s}" if limit.to_i > 0
query << "marker=#{CloudFiles.escape marker.to_s}" unless marker.to_s.empty?
response = cfreq("GET", @storagehost, "#{@storagepath}?#{query.join '&'}", @storageport, @storagescheme)
response = storage_request("GET", "?#{query.join('&')}")
return {} if (response.code == "204")
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "200")
doc = REXML::Document.new(response.body)
Expand All @@ -208,7 +208,7 @@ def containers_detail(limit = 0, marker = "")
# cf.container_exists?('bad_container')
# => false
def container_exists?(containername)
response = cfreq("HEAD", @storagehost, "#{@storagepath}/#{CloudFiles.escape containername}", @storageport, @storagescheme)
response = storage_request("HEAD", CloudFiles.escape(containername))
return (response.code == "204")? true : false ;
end

Expand All @@ -227,7 +227,7 @@ def container_exists?(containername)
def create_container(containername)
raise CloudFiles::Exception::Syntax, "Container name cannot contain the characters '/' or '?'" if containername.match(/[\/\?]/)
raise CloudFiles::Exception::Syntax, "Container name is limited to 256 characters" if containername.length > 256
response = cfreq("PUT", @storagehost, "#{@storagepath}/#{CloudFiles.escape containername}", @storageport, @storagescheme)
response = storage_request("PUT", CloudFiles.escape(containername))
raise CloudFiles::Exception::InvalidResponse, "Unable to create container #{containername}" unless (response.code == "201" || response.code == "202")
CloudFiles::Container.new(self, containername)
end
Expand All @@ -244,7 +244,7 @@ def create_container(containername)
# cf.delete_container('nonexistent')
# => NoSuchContainerException: Container nonexistent does not exist
def delete_container(containername)
response = cfreq("DELETE", @storagehost, "#{@storagepath}/#{CloudFiles.escape containername}", @storageport, @storagescheme)
response = storage_request("DELETE", CloudFiles.escape(containername))
raise CloudFiles::Exception::NonEmptyContainer, "Container #{containername} is not empty" if (response.code == "409")
raise CloudFiles::Exception::NoSuchContainer, "Container #{containername} does not exist" unless (response.code == "204")
true
Expand All @@ -261,12 +261,22 @@ def delete_container(containername)
# => ["video", "webpics"]
def public_containers(enabled_only = false)
paramstr = enabled_only == true ? "enabled_only=true" : ""
response = cfreq("GET", @cdnmgmthost, "#{@cdnmgmtpath}?#{paramstr}", @cdnmgmtport, @cdnmgmtscheme)
response = cdn_request("GET", "?#{paramstr}")
return [] if (response.code == "204")
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "200")
CloudFiles.lines(response.body)
end

def storage_request(method, path = "", headers = {}, data = nil, attempts = 0, &block)
path = "#{@storagepath}/#{path}" unless (path[0,1] == '/')
cfreq(method, @storagehost, path, @storageport, @storagescheme, headers, data, attempts, &block)
end

def cdn_request(method, path = "", headers = {}, data = nil, attempts = 0, &block)
path = "#{@cdnmgmtpath}/#{path}" unless (path[0,1] == '/')
cfreq(method, @cdnmgmthost, path, @cdnmgmtport, @cdnmgmtscheme, headers, data, attempts, &block)
end

# This method actually makes the HTTP calls out to the server
def cfreq(method, server, path, port, scheme, headers = {}, data = nil, attempts = 0, &block) # :nodoc:
start = Time.now
Expand Down
84 changes: 53 additions & 31 deletions lib/cloudfiles/container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,6 @@ class Container
def initialize(connection, name)
@connection = connection
@name = name
@storagehost = self.connection.storagehost
@storagepath = self.connection.storagepath + "/" + CloudFiles.escape(@name)
@storageport = self.connection.storageport
@storagescheme = self.connection.storagescheme
if self.connection.cdn_available?
@cdnmgmthost = self.connection.cdnmgmthost
@cdnmgmtpath = self.connection.cdnmgmtpath + "/" + CloudFiles.escape(@name) if self.connection.cdnmgmtpath
@cdnmgmtport = self.connection.cdnmgmtport
@cdnmgmtscheme = self.connection.cdnmgmtscheme
end
# Load the metadata now, so we'll get a CloudFiles::Exception::NoSuchContainer exception should the container
# not exist.
self.container_metadata
Expand All @@ -51,11 +41,11 @@ def refresh
# Retrieves Metadata for the container
def container_metadata
@metadata ||= (
response = self.connection.cfreq("HEAD", @storagehost, @storagepath + "/", @storageport, @storagescheme)
response = self.connection.storage_request("HEAD", "#{escaped_name}/")
raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist" unless (response.code =~ /^20/)
resphash = {}
response.to_hash.select { |k,v| k.match(/^x-container-meta/) }.each { |x| resphash[x[0]] = x[1].to_s }
{:bytes => response["x-container-bytes-used"].to_i, :count => response["x-container-object-count"].to_i, :metadata => resphash}
{:bytes => response["x-container-bytes-used"].to_i, :count => response["x-container-object-count"].to_i, :metadata => resphash, :container_read => response["x-container-read"], :container_write => response["x-container-write"]}
)
end

Expand All @@ -64,7 +54,7 @@ def cdn_metadata
return @cdn_metadata if @cdn_metadata
if cdn_available?
@cdn_metadata = (
response = self.connection.cfreq("HEAD", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme)
response = self.connection.cdn_request("HEAD", escaped_name)
cdn_enabled = ((response["x-cdn-enabled"] || "").downcase == "true") ? true : false
{
:cdn_enabled => cdn_enabled,
Expand Down Expand Up @@ -100,7 +90,7 @@ def metadata
def set_metadata(metadatahash)
headers = {}
metadatahash.each{ |key, value| headers['X-Container-Meta-' + CloudFiles.escape(key.to_s.capitalize)] = value.to_s }
response = self.connection.cfreq("POST", @storagehost, @storagepath, @storageport, @storagescheme, headers)
response = self.connection.storage_request("POST", escaped_name, headers)
raise CloudFiles::Exception::NoSuchObject, "Container #{@name} does not exist" if (response.code == "404")
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code =~ /^20/)
true
Expand Down Expand Up @@ -156,6 +146,16 @@ def referrer_acl
self.cdn_metadata[:referrer_acl]
end

#used by openstack swift
def read_acl
self.container_metadata[:container_read]
end

#used by openstack swift
def write_acl
self.container_metadata[:container_write]
end

# Returns true if log retention is enabled on this container, false otherwise
def cdn_log
self.cdn_metadata[:cdn_log]
Expand All @@ -166,10 +166,10 @@ def cdn_log
# Change the log retention status for this container. Values are true or false.
#
# These logs will be periodically (at unpredictable intervals) compressed and uploaded
# to a .CDN_ACCESS_LOGS container in the form of container_name.YYYYMMDDHH-XXXX.gz.
# to a ".CDN_ACCESS_LOGS" container in the form of "container_name.YYYYMMDDHH-XXXX.gz".
def log_retention=(value)
raise Exception::CDNNotAvailable unless cdn_available?
response = self.connection.cfreq("POST", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme, {"x-log-retention" => value.to_s.capitalize})
response = self.connection.cdn_request("POST", escaped_name, {"x-log-retention" => value.to_s.capitalize})
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "201" or response.code == "202")
return true
end
Expand Down Expand Up @@ -216,7 +216,7 @@ def objects(params = {})
query << "#{param}=#{CloudFiles.escape(value.to_s)}"
end
end
response = self.connection.cfreq("GET", @storagehost, "#{@storagepath}?#{query.join '&'}", @storageport, @storagescheme)
response = self.connection.storage_request("GET", "#{escaped_name}?#{query.join '&'}")
return [] if (response.code == "204")
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "200")
return CloudFiles.lines(response.body)
Expand Down Expand Up @@ -249,7 +249,7 @@ def objects_detail(params = {})
query << "#{param}=#{CloudFiles.escape(value.to_s)}"
end
end
response = self.connection.cfreq("GET", @storagehost, "#{@storagepath}?#{query.join '&'}", @storageport, @storagescheme)
response = self.connection.storage_request("GET", "#{escaped_name}?#{query.join '&'}")
return {} if (response.code == "204")
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code == "200")
doc = REXML::Document.new(response.body)
Expand Down Expand Up @@ -281,7 +281,7 @@ def empty?
# container.object_exists?('badfile.txt')
# => false
def object_exists?(objectname)
response = self.connection.cfreq("HEAD", @storagehost, "#{@storagepath}/#{CloudFiles.escape objectname}", @storageport, @storagescheme)
response = self.connection.storage_request("HEAD", "#{escaped_name}/#{CloudFiles.escape objectname}")
return (response.code =~ /^20/)? true : false
end

Expand All @@ -306,7 +306,7 @@ def create_object(objectname, make_path = false)
# container.delete_object('nonexistent_file.txt')
# => NoSuchObjectException: Object nonexistent_file.txt does not exist
def delete_object(objectname)
response = self.connection.cfreq("DELETE", @storagehost, "#{@storagepath}/#{CloudFiles.escape objectname}", @storageport, @storagescheme)
response = self.connection.storage_request("DELETE", "#{escaped_name}/#{CloudFiles.escape objectname}")
raise CloudFiles::Exception::NoSuchObject, "Object #{objectname} does not exist" if (response.code == "404")
raise CloudFiles::Exception::InvalidResponse, "Invalid response code #{response.code}" unless (response.code =~ /^20/)
true
Expand All @@ -333,17 +333,39 @@ def make_public(options = {:ttl => 86400})
options = {:ttl => ttl}
end

response = self.connection.cfreq("PUT", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme)
response = self.connection.cdn_request("PUT", escaped_name)
raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist" unless (response.code == "201" || response.code == "202")

headers = { "X-TTL" => options[:ttl].to_s , "X-CDN-Enabled" => "True" }
headers["X-User-Agent-ACL"] = options[:user_agent_acl] if options[:user_agent_acl]
headers["X-Referrer-ACL"] = options[:referrer_acl] if options[:referrer_acl]
response = self.connection.cfreq("POST", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme, headers)
response = post_with_headers(headers)
raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist" unless (response.code == "201" || response.code == "202")
refresh
true
end

# Only to be used with openstack swift
def set_write_acl(write_string)
headers = {"X-Container-Write" => write_string}
post_with_headers(headers)
end

# Only to be used with openstack swift
def set_read_acl(read_string)
headers = {"X-Container-Read" => read_string}
post_with_headers(headers)
end

def post_with_headers(headers = {})
if cdn_enabled?
response = self.connection.cdn_request("POST", escaped_name, headers)
else
response = self.connection.storage_request("POST", escaped_name, headers)
end
raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist" unless (response.code =~ /^20/)
response
end

# Makes a container private and returns true upon success. Throws NoSuchContainerException
# if the container doesn't exist or if the request fails.
Expand All @@ -355,7 +377,7 @@ def make_public(options = {:ttl => 86400})
def make_private
raise Exception::CDNNotAvailable unless cdn_available?
headers = { "X-CDN-Enabled" => "False" }
response = self.connection.cfreq("POST", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme, headers)
response = self.connection.cdn_request("POST", escaped_name, headers)
raise CloudFiles::Exception::NoSuchContainer, "Container #{@name} does not exist" unless (response.code == "201" || response.code == "202")
refresh
true
Expand All @@ -380,15 +402,11 @@ def make_private
# => true
def purge_from_cdn(email=nil)
raise Exception::CDNNotAvailable unless cdn_available?
if email
headers = {"X-Purge-Email" => email}
response = self.connection.cfreq("DELETE", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme, headers)
raise CloudFiles::Exception::Connection, "Error Unable to Purge Container: #{@name}" unless (response.code > "200" && response.code < "299")
else
response = self.connection.cfreq("DELETE", @cdnmgmthost, @cdnmgmtpath, @cdnmgmtport, @cdnmgmtscheme)
raise CloudFiles::Exception::Connection, "Error Unable to Purge Container: #{@name}" unless (response.code > "200" && response.code < "299")
headers = {}
headers = {"X-Purge-Email" => email} if email
response = self.connection.cdn_request("DELETE", escaped_name, headers)
raise CloudFiles::Exception::Connection, "Error Unable to Purge Container: #{@name}" unless (response.code > "200" && response.code < "299")
true
end
end

def to_s # :nodoc:
Expand All @@ -398,6 +416,10 @@ def to_s # :nodoc:
def cdn_available?
self.connection.cdn_available?
end

def escaped_name
CloudFiles.escape(@name)
end

end

Expand Down
Loading

0 comments on commit ea9aa70

Please sign in to comment.