1
0
Fork 0
mirror of https://github.com/fog/fog.git synced 2022-11-09 13:51:43 -05:00

Merge pull request #1563 from rackspace/dir_metadata

[Rackspace|Storage] Added support for container metadata
This commit is contained in:
Brian Hartsock 2013-02-21 17:54:43 -08:00
commit 67650e2b3e
8 changed files with 267 additions and 5 deletions

View file

@ -26,8 +26,11 @@ module Fog
directory.merge_attributes(key => value)
end
end
directory.metadata = Metadata.from_headers(data.headers)
directory.files.merge_attributes(options)
directory.files.instance_variable_set(:@loaded, true)
data.body.each do |file|
directory.files << directory.files.new(file)
end

View file

@ -1,5 +1,6 @@
require 'fog/core/model'
require 'fog/rackspace/models/storage/files'
require 'fog/rackspace/models/storage/metadata'
module Fog
module Storage
@ -9,10 +10,27 @@ module Fog
identity :key, :aliases => 'name'
attribute :bytes, :aliases => 'X-Container-Bytes-Used'
attribute :count, :aliases => 'X-Container-Object-Count'
attribute :bytes, :aliases => 'X-Container-Bytes-Used', :type => :integer
attribute :count, :aliases => 'X-Container-Object-Count', :type => :integer
attribute :cdn_cname
def metadata=(hash)
if hash.is_a? Fog::Storage::Rackspace::Metadata
@metadata = hash
else
@metadata = Fog::Storage::Rackspace::Metadata.new(hash)
end
@metadata
end
def metadata
unless @metadata
response = service.head_container(key)
@metadata = Fog::Storage::Rackspace::Metadata.from_headers(response.headers)
end
@metadata
end
def destroy
requires :key
service.delete_container(key)
@ -58,7 +76,8 @@ module Fog
def save
requires :key
service.put_container(key)
headers = @metadata.nil? ? {} : metadata.to_headers
service.put_container(key, headers)
if service.cdn && public?
# if public and CDN connection then update cdn to public

View file

@ -0,0 +1,79 @@
module Fog
module Storage
class Rackspace
class Metadata
# META_PREFIX = "X-Object-Meta-"
# REMOVE_META_PREFIX = "X-Remove-Object-Meta-"
META_PREFIX = "X-Container-Meta-"
REMOVE_META_PREFIX = "X-Remove-Container-Meta-"
# Cloud Files will ignore headers without a value
DUMMY_VALUE = 1
KEY_REGEX = /^#{META_PREFIX}(.*)/
attr_reader :data
def initialize(hash={})
@data = hash || {}
@deleted_hash = {}
end
def delete(key)
data.delete(key)
@deleted_hash[key] = nil
end
def to_headers
headers = {}
h = data.merge(@deleted_hash)
h.each_pair do |k,v|
key = to_header_key(k,v)
headers[key] = v || DUMMY_VALUE
end
headers
end
def self.from_headers(headers)
metadata = Metadata.new
headers.each_pair do |k, v|
key = Metadata.to_key(k)
next unless key
metadata.data[key] = Fog::JSON.decode(v)
end
metadata
end
def respond_to?(method_sym, include_private = false)
super(method_sym, include_private) || data.respond_to?(method_sym, include_private)
end
def method_missing(method, *args, &block)
data.send(method, *args, &block)
end
private
def self.to_key(key)
m = key.match KEY_REGEX
return nil unless m && m[1]
a = m[1].split('-')
a.collect!(&:downcase)
str = a.join('_')
str.to_sym
end
def to_header_key(key, value)
prefix = value.nil? ? REMOVE_META_PREFIX : META_PREFIX
prefix + key.to_s.split(/[-_]/).collect(&:capitalize).join('-')
end
end
end
end
end

View file

@ -8,10 +8,11 @@ module Fog
# ==== Parameters
# * name<~String> - Name for container, should be < 256 bytes and must not contain '/'
#
def put_container(name)
def put_container(name, options={})
request(
:expects => [201, 202],
:method => 'PUT',
:headers => options,
:path => Fog::Rackspace.escape(name)
)
end

View file

@ -0,0 +1,29 @@
Shindo.tests('Fog::Rackspace::Storage | directories', ['rackspace']) do
pending if Fog.mocking?
@service = Fog::Storage[:rackspace]
begin
@name = "fog-directories-test-#{Time.now.to_i.to_s}"
@filename = 'lorem.txt'
@dir = @service.directories.create :key => @name, :metadata => {:fog_test => true}
@file = @dir.files.create :key => @filename, :body => lorem_file
tests('#get').succeeds do
instance = @service.directories.get @name
returns(false) { instance.nil? }
returns(true) { instance.metadata[:fog_test] }
returns(@name) { instance.key }
returns(1) { instance.count }
returns( Fog::Storage.get_body_size(lorem_file)) {instance.bytes }
returns(@filename) { instance.files.first.key }
end
ensure
@file.destroy if @file
@dir.destroy if @dir
end
end

View file

@ -0,0 +1,56 @@
Shindo.tests('Fog::Rackspace::Storage | directory', ['rackspace']) do
pending if Fog.mocking?
@service = Fog::Storage[:rackspace]
def container_meta_attributes
@service.head_container(@instance.key).headers.reject {|k, v| !(k =~ /X-Container-Meta-/)}
end
directory_attributes = {
# Add a random suffix to prevent collision
:key => "fog-directory-tests-#{rand(65536)}"
}
model_tests(@service.directories, directory_attributes, Fog.mocking?) do
tests('#public_url').returns(nil) do
@instance.public_url
end
end
directory_attributes[:metadata] = {:draft => 'true'}
tests('metadata') do
pending if Fog.mocking?
model_tests(@service.directories, directory_attributes, Fog.mocking?) do
tests('sets metadata on create').returns({:draft => 'true'}) do
@instance.metadata.data
end
tests('update metadata').returns({"X-Container-Meta-Draft"=>"true", "X-Container-Meta-Color"=>"green"}) do
@instance.metadata[:color] = 'green'
@instance.save
container_meta_attributes
end
tests('set metadata to nil').returns({"X-Container-Meta-Draft"=>"true"}) do
@instance.metadata[:color] = nil
@instance.save
container_meta_attributes
end
tests('delete metadata').returns({}) do
@instance.metadata.delete(:draft)
@instance.save
container_meta_attributes
end
tests('should retrieve metadata when necessary') do
@service.put_container(@instance.key, {"X-Container-Meta-List-Test"=>"true"} )
dir = @service.directories.find {|d| d.key == @instance.key }
returns(nil) { dir.instance_variable_get("@metadata") }
returns(true) { dir.metadata[:list_test] }
end
end
end
end

View file

@ -0,0 +1,68 @@
require 'fog/rackspace/models/storage/metadata'
Shindo.tests('Fog::Rackspace::Storage | metadata', ['rackspace']) do
tests('#to_key') do
tests('valid key').returns(:image_size) do
Fog::Storage::Rackspace::Metadata.send(:to_key, "X-Container-Meta-Image-Size")
end
tests('invalid key').returns(nil) do
Fog::Storage::Rackspace::Metadata.send(:to_key, "bad-key")
end
end
tests('#to_header_key') do
metadata = Fog::Storage::Rackspace::Metadata.new
tests('key to add').returns("X-Container-Meta-Thumbnail-Image") do
metadata.send(:to_header_key, :thumbnail_image, true)
end
tests('key to remove').returns("X-Remove-Container-Meta-Thumbnail-Image") do
metadata.send(:to_header_key, :thumbnail_image, nil)
end
end
tests('#to_headers').returns({"X-Container-Meta-Preview"=>true, "X-Remove-Container-Meta-Delete-Me"=>1}) do
metadata = Fog::Storage::Rackspace::Metadata.new
metadata[:preview] = true
metadata[:delete_me] = nil
metadata.to_headers
end
tests("#from_headers").returns({:my_boolean=>true, :my_integer=>42, :my_string=>"I am a string"}) do
headers = {
"X-Container-Meta-My-Integer"=> "42",
"X-Container-Meta-My-Boolean"=> "true",
"X-Container-Meta-My-String"=> "\"I am a string\""
}
metadata = Fog::Storage::Rackspace::Metadata.from_headers headers
metadata.data
end
tests("#delete").returns({"X-Remove-Container-Meta-Delete-Me"=>1}) do
metadata = Fog::Storage::Rackspace::Metadata.new
metadata.delete(:delete_me)
metadata.to_headers
end
tests("#respond_to?") do
tests('Should respond to all of the methods in Hash class').returns(true) do
metadata = Fog::Storage::Rackspace::Metadata.new
Hash.instance_methods.all? {|method| metadata.respond_to?(method)}
end
tests('Should respond to all of the methods in the Metadata class').returns(true) do
metadata = Fog::Storage::Rackspace::Metadata.new
metadata.methods.all? {|method| metadata.respond_to?(method)}
end
end
tests("#method_missing").returns(true) do
metadata = Fog::Storage::Rackspace::Metadata.new
metadata[:test] = true
metadata[:test]
end
end

View file

@ -10,11 +10,18 @@ Shindo.tests('Fog::Storage[:rackspace] | container requests', ["rackspace"]) do
tests('success') do
tests("#put_container('fogcontainertests')").succeeds do
tests("#put_container('fogcontainertests', {})").succeeds do
pending if Fog.mocking?
Fog::Storage[:rackspace].put_container('fogcontainertests')
end
tests("#put_container('fogcontainertests', 'X-Container-Meta-Color'=>'green')").succeeds do
pending if Fog.mocking?
Fog::Storage[:rackspace].put_container('fogcontainertests', 'X-Container-Meta-Color'=>'green')
response = Fog::Storage[:rackspace].head_container('fogcontainertests')
returns('green') { response.headers['X-Container-Meta-Color'] }
end
tests("#get_container('fogcontainertests')").formats(@container_format) do
pending if Fog.mocking?
Fog::Storage[:rackspace].get_container('fogcontainertests').body