diff --git a/lib/fog/joyent/models/compute/server.rb b/lib/fog/joyent/models/compute/server.rb index 69bcb1012..b33c28460 100644 --- a/lib/fog/joyent/models/compute/server.rb +++ b/lib/fog/joyent/models/compute/server.rb @@ -53,7 +53,7 @@ module Fog def resize(flavor) requires :id - service.resize_machine(id, flavor) + service.resize_machine(id, flavor.name) true end diff --git a/lib/fog/joyent/requests/compute/resize_machine.rb b/lib/fog/joyent/requests/compute/resize_machine.rb index 78a9edcbe..0c2a63933 100644 --- a/lib/fog/joyent/requests/compute/resize_machine.rb +++ b/lib/fog/joyent/requests/compute/resize_machine.rb @@ -1,6 +1,7 @@ module Fog module Compute class Joyent + class Real def resize_machine(id, package) request( :method => "POST", @@ -8,6 +9,7 @@ module Fog :query => {"action" => "resize", "package" => package} ) end + end end end end diff --git a/lib/fog/openstack/models/network/network.rb b/lib/fog/openstack/models/network/network.rb index 5f4d7b2b9..1cf80eaac 100644 --- a/lib/fog/openstack/models/network/network.rb +++ b/lib/fog/openstack/models/network/network.rb @@ -12,6 +12,14 @@ module Fog attribute :status attribute :admin_state_up attribute :tenant_id + attribute :provider_network_type, + :aliases => 'provider:network_type' + attribute :provider_physical_network, + :aliases => 'provider:physical_network' + attribute :provider_segmentation_id, + :aliases => 'provider:segmentation_id' + attribute :router_external, + :aliases => 'router:external' def initialize(attributes) # Old 'connection' is renamed as service and should be used instead diff --git a/lib/fog/rackspace/mock_data.rb b/lib/fog/rackspace/mock_data.rb index 41dec5aa9..008e180f4 100644 --- a/lib/fog/rackspace/mock_data.rb +++ b/lib/fog/rackspace/mock_data.rb @@ -1,6 +1,9 @@ module Fog module Rackspace module MockData + + NOT_FOUND_ID = "NOT-FOUND" + def data @@data ||= Hash.new do |hash, key| hash[key] = begin @@ -101,12 +104,13 @@ module Fog } #Mock Data Hash - { + h = { #Compute V2 - :flavors => {flavor_id => flavor}, - :images => {image_id => image}, + :flavors => Hash.new { |h,k| h[k] = flavor unless k == NOT_FOUND_ID}, + :images => Hash.new { |h,k| h[k] = image unless k == NOT_FOUND_ID }, + :networks => Hash.new { |h,k| h[k] = network unless k == NOT_FOUND_ID }, + :servers => {}, - :networks => { network_id => network }, #Block Storage :volumes => {}, @@ -114,6 +118,13 @@ module Fog :volume_attachments => [], :volume_types => {volume_type1_id => volume_type1, volume_type2_id => volume_type2}, } + + # seed with initial data + h[:flavors][flavor_id] = flavor + h[:images][image_id] = image + h[:networks][network_id] = network + + h end end[@rackspace_api_key] end diff --git a/lib/fog/rackspace/models/compute_v2/servers.rb b/lib/fog/rackspace/models/compute_v2/servers.rb index 6aea751e3..47ba9cd76 100644 --- a/lib/fog/rackspace/models/compute_v2/servers.rb +++ b/lib/fog/rackspace/models/compute_v2/servers.rb @@ -19,6 +19,8 @@ module Fog end # Creates a new server and populates ssh keys + # @note This method is incompatible with Cloud Servers utlizing RackConnect. RackConnect users + # should use server personalization to install keys. Please see Server#personality for more information. # @example # service = Fog::Compute.new(:provider => 'rackspace', # :version => :v2, diff --git a/lib/fog/rackspace/models/storage/directories.rb b/lib/fog/rackspace/models/storage/directories.rb index 56248888a..295f92dc7 100644 --- a/lib/fog/rackspace/models/storage/directories.rb +++ b/lib/fog/rackspace/models/storage/directories.rb @@ -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 diff --git a/lib/fog/rackspace/models/storage/directory.rb b/lib/fog/rackspace/models/storage/directory.rb index 3d73bf28b..8301116ff 100644 --- a/lib/fog/rackspace/models/storage/directory.rb +++ b/lib/fog/rackspace/models/storage/directory.rb @@ -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,12 +10,29 @@ 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 attr_writer :public, :public_url + 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) @@ -63,8 +81,8 @@ module Fog def save requires :key - create_container(key) + create_container raise Fog::Storage::Rackspace::Error.new("Directory can not be set as :public without a CDN provided") if public? && !cdn_enabled? @urls = service.cdn.publish_container(self, public?) true @@ -82,8 +100,9 @@ module Fog @urls ||= service.cdn.urls(self) end - def create_container(key) - service.put_container(key) + def create_container + headers = @metadata.nil? ? {} : metadata.to_headers + service.put_container(key, headers) end end end diff --git a/lib/fog/rackspace/models/storage/metadata.rb b/lib/fog/rackspace/models/storage/metadata.rb new file mode 100644 index 000000000..d586e964b --- /dev/null +++ b/lib/fog/rackspace/models/storage/metadata.rb @@ -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 \ No newline at end of file diff --git a/lib/fog/rackspace/requests/storage/put_container.rb b/lib/fog/rackspace/requests/storage/put_container.rb index 32adc8003..c47d39dd7 100644 --- a/lib/fog/rackspace/requests/storage/put_container.rb +++ b/lib/fog/rackspace/requests/storage/put_container.rb @@ -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 diff --git a/tests/openstack/models/network/network_tests.rb b/tests/openstack/models/network/network_tests.rb index 785a324b5..28491f9f2 100644 --- a/tests/openstack/models/network/network_tests.rb +++ b/tests/openstack/models/network/network_tests.rb @@ -1,7 +1,7 @@ Shindo.tests("Fog::Network[:openstack] | network", ['openstack']) do tests('success') do - + tests('#create').succeeds do @instance = Fog::Network[:openstack].networks.create(:name => 'net_name', :shared => false, @@ -9,6 +9,26 @@ Shindo.tests("Fog::Network[:openstack] | network", ['openstack']) do :tenant_id => 'tenant_id') !@instance.id.nil? end + + tests('have attributes') do + attributes = [ + :name, + :subnets, + :shared, + :status, + :admin_state_up, + :tenant_id, + :provider_network_type, + :provider_physical_network, + :provider_segmentation_id, + :router_external + ] + tests("The network model should respond to") do + attributes.each do |attribute| + test("#{attribute}") { @instance.respond_to? attribute } + end + end + end tests('#update').succeeds do @instance.name = 'new_net_name' diff --git a/tests/rackspace/models/compute_v2/flavors_tests.rb b/tests/rackspace/models/compute_v2/flavors_tests.rb index d27d13d69..bb7786cb3 100644 --- a/tests/rackspace/models/compute_v2/flavors_tests.rb +++ b/tests/rackspace/models/compute_v2/flavors_tests.rb @@ -12,6 +12,6 @@ Shindo.tests('Fog::Compute::RackspaceV2 | flavors', ['rackspace']) do end tests("failure").returns(nil) do - service.flavors.get('some_random_identity') + service.flavors.get(Fog::Rackspace::MockData::NOT_FOUND_ID) end end diff --git a/tests/rackspace/models/compute_v2/images_tests.rb b/tests/rackspace/models/compute_v2/images_tests.rb index a0bb4a311..8689efddd 100644 --- a/tests/rackspace/models/compute_v2/images_tests.rb +++ b/tests/rackspace/models/compute_v2/images_tests.rb @@ -15,6 +15,6 @@ Shindo.tests('Fog::Compute::RackspaceV2 | images', ['rackspace']) do end tests("failure").returns(nil) do - service.images.get('some_random_identity') + service.images.get(Fog::Rackspace::MockData::NOT_FOUND_ID) end end diff --git a/tests/rackspace/models/storage/directories_tests.rb b/tests/rackspace/models/storage/directories_tests.rb new file mode 100644 index 000000000..acb5ee773 --- /dev/null +++ b/tests/rackspace/models/storage/directories_tests.rb @@ -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 \ No newline at end of file diff --git a/tests/rackspace/models/storage/directory_tests.rb b/tests/rackspace/models/storage/directory_tests.rb index d3c2da38c..01103e802 100644 --- a/tests/rackspace/models/storage/directory_tests.rb +++ b/tests/rackspace/models/storage/directory_tests.rb @@ -1,15 +1,19 @@ -Shindo.tests('Fog::Rackspace::Storage | directories', ['rackspace']) do +Shindo.tests('Fog::Rackspace::Storage | directory', ['rackspace']) do pending if Fog.mocking? - service = Fog::Storage[:rackspace] + @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 + model_tests(@service.directories, directory_attributes, Fog.mocking?) do tests('#public?').returns(false) do @instance.public? @@ -27,11 +31,11 @@ Shindo.tests('Fog::Rackspace::Storage | directories', ['rackspace']) do end @instance.cdn_cname = nil - service.instance_variable_set "@rackspace_cdn_ssl", true + @service.instance_variable_set "@rackspace_cdn_ssl", true tests('ssl').returns(nil) do @instance.public_url end - service.instance_variable_set "@rackspace_cdn_ssl", nil + @service.instance_variable_set "@rackspace_cdn_ssl", nil end tests('#ios_url').returns(nil) do @@ -62,11 +66,11 @@ Shindo.tests('Fog::Rackspace::Storage | directories', ['rackspace']) do end @instance.cdn_cname = nil - service.instance_variable_set "@rackspace_cdn_ssl", true + @service.instance_variable_set "@rackspace_cdn_ssl", true tests('ssl').returns(0) do @instance.public_url =~ /https:\/\/.+\.ssl\./ end - service.instance_variable_set "@rackspace_cdn_ssl", nil + @service.instance_variable_set "@rackspace_cdn_ssl", nil end tests('#ios_url').returns(0) do @@ -85,4 +89,40 @@ Shindo.tests('Fog::Rackspace::Storage | directories', ['rackspace']) do returns(nil) { @instance.instance_variable_get("@public") } 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 \ No newline at end of file diff --git a/tests/rackspace/models/storage/metadata_tests.rb b/tests/rackspace/models/storage/metadata_tests.rb new file mode 100644 index 000000000..7978a64fd --- /dev/null +++ b/tests/rackspace/models/storage/metadata_tests.rb @@ -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 \ No newline at end of file diff --git a/tests/rackspace/requests/compute/image_tests.rb b/tests/rackspace/requests/compute/image_tests.rb index 5521ded2c..582b894b9 100644 --- a/tests/rackspace/requests/compute/image_tests.rb +++ b/tests/rackspace/requests/compute/image_tests.rb @@ -57,12 +57,12 @@ Shindo.tests('Fog::Compute[:rackspace] | image requests', ['rackspace']) do tests('failure') do tests('#delete_image(0)').raises(Excon::Errors::BadRequest) do - @service.delete_image(0) + @service.delete_image(Fog::Rackspace::MockData::NOT_FOUND_ID) end tests('#get_image_details(0)').raises(Fog::Compute::Rackspace::NotFound) do pending if Fog.mocking? - @service.get_image_details(0) + @service.get_image_details(Fog::Rackspace::MockData::NOT_FOUND_ID) end end diff --git a/tests/rackspace/requests/compute_v2/image_tests.rb b/tests/rackspace/requests/compute_v2/image_tests.rb index d375d62a9..4fe6ec5d2 100644 --- a/tests/rackspace/requests/compute_v2/image_tests.rb +++ b/tests/rackspace/requests/compute_v2/image_tests.rb @@ -59,11 +59,11 @@ Shindo.tests('Fog::Compute::RackspaceV2 | image_tests', ['rackspace']) do tests('failure') do tests('#delete_image').raises(Excon::Errors::BadRequest) do - Fog::Compute[:rackspace].delete_image(0) + Fog::Compute[:rackspace].delete_image(Fog::Rackspace::MockData::NOT_FOUND_ID) end tests('#get_image').raises(Fog::Compute::RackspaceV2::NotFound) do - service.get_image(0) + service.get_image(Fog::Rackspace::MockData::NOT_FOUND_ID) end end ensure diff --git a/tests/rackspace/requests/storage/container_tests.rb b/tests/rackspace/requests/storage/container_tests.rb index 93168bfaa..1b46bde71 100644 --- a/tests/rackspace/requests/storage/container_tests.rb +++ b/tests/rackspace/requests/storage/container_tests.rb @@ -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