From c9e38788d57250b54f516075163e4a4aad06fda3 Mon Sep 17 00:00:00 2001 From: "Brian D. Burns" Date: Tue, 2 Jul 2013 18:20:57 -0400 Subject: [PATCH] [rackspace|storage] add #delete_multiple_objects --- .../storage/delete_multiple_objects.rb | 72 +++++++++++++++++++ lib/fog/rackspace/storage.rb | 1 + .../requests/storage/object_tests.rb | 61 ++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 lib/fog/rackspace/requests/storage/delete_multiple_objects.rb diff --git a/lib/fog/rackspace/requests/storage/delete_multiple_objects.rb b/lib/fog/rackspace/requests/storage/delete_multiple_objects.rb new file mode 100644 index 000000000..ec75038fb --- /dev/null +++ b/lib/fog/rackspace/requests/storage/delete_multiple_objects.rb @@ -0,0 +1,72 @@ +module Fog + module Storage + class Rackspace + class Real + + # Deletes multiple objects or containers with a single request. + # + # To delete objects from a single container, +container+ may be provided + # and +object_names+ should be an Array of object names within the container. + # + # To delete objects from multiple containers or delete containers, + # +container+ should be +nil+ and all +object_names+ should be prefixed with a container name. + # + # Containers must be empty when deleted. +object_names+ are processed in the order given, + # so objects within a container should be listed first to empty the container. + # + # Up to 10,000 objects may be deleted in a single request. + # The server will respond with +200 OK+ for all requests. + # +response.body+ must be inspected for actual results. + # + # @example Delete objects from a container + # object_names = ['object', 'another/object'] + # conn.delete_multiple_objects('my_container', object_names) + # + # @example Delete objects from multiple containers + # object_names = ['container_a/object', 'container_b/object'] + # conn.delete_multiple_objects(nil, object_names) + # + # @example Delete a container and all it's objects + # object_names = ['my_container/object_a', 'my_container/object_b', 'my_container'] + # conn.delete_multiple_objects(nil, object_names) + # + # @param container [String,nil] Name of container. + # @param object_names [Array] Object names to be deleted. + # @param options [Hash] Additional request headers. + # + # @return [Excon::Response] + # * body [Hash] - Results of the operation. + # * "Number Not Found" [Integer] - Number of missing objects or containers. + # * "Response Status" [String] - Response code for the subrequest of the last failed operation. + # * "Errors" [Array] + # * object_name [String] - Object that generated an error when the delete was attempted. + # * response_status [String] - Response status from the subrequest for object_name. + # * "Number Deleted" [Integer] - Number of objects or containers deleted. + # * "Response Body" [String] - Response body for "Response Status". + # + # @raise [Fog::Storage::Rackspace::NotFound] HTTP 404 + # @raise [Fog::Storage::Rackspace::BadRequest] HTTP 400 + # @raise [Fog::Storage::Rackspace::InternalServerError] HTTP 500 + # @raise [Fog::Storage::Rackspace::ServiceError] + # @raise [Excon::Errors::Unauthorized] HTTP 401 + # + # @see http://docs.rackspace.com/files/api/v1/cf-devguide/content/Bulk_Delete-d1e2338.html + def delete_multiple_objects(container, object_names, options = {}) + body = object_names.map do |name| + object_name = container ? "#{ container }/#{ name }" : name + URI.encode(object_name) + end.join("\n") + + request( + :expects => 200, + :method => 'DELETE', + :headers => options.merge('Content-Type' => 'text/plain'), + :body => body, + :query => { 'bulk-delete' => true } + ) + end + + end + end + end +end diff --git a/lib/fog/rackspace/storage.rb b/lib/fog/rackspace/storage.rb index 129182f15..f48f9e1a8 100644 --- a/lib/fog/rackspace/storage.rb +++ b/lib/fog/rackspace/storage.rb @@ -26,6 +26,7 @@ module Fog request :delete_container request :delete_object request :delete_static_large_object + request :delete_multiple_objects request :get_container request :get_containers request :get_object diff --git a/tests/rackspace/requests/storage/object_tests.rb b/tests/rackspace/requests/storage/object_tests.rb index 504fc39f5..0a65518a2 100644 --- a/tests/rackspace/requests/storage/object_tests.rb +++ b/tests/rackspace/requests/storage/object_tests.rb @@ -98,6 +98,31 @@ Shindo.tests('Fog::Storage[:rackspace] | object requests', ["rackspace"]) do end end + tests('#delete_multiple_objects') do + pending if Fog.mocking? + + Fog::Storage[:rackspace].put_object('fogobjecttests', 'fog_object', lorem_file) + Fog::Storage[:rackspace].put_object('fogobjecttests', 'fog_object2', lorem_file) + Fog::Storage[:rackspace].directories.create(:key => 'fogobjecttests2') + Fog::Storage[:rackspace].put_object('fogobjecttests2', 'fog_object', lorem_file) + + expected = { + "Number Not Found" => 0, + "Response Status" => "200 OK", + "Errors" => [], + "Number Deleted" => 2, + "Response Body" => "" + } + + returns(expected, 'deletes multiple objects') do + Fog::Storage[:rackspace].delete_multiple_objects('fogobjecttests', ['fog_object', 'fog_object2']).body + end + + returns(expected, 'deletes object and container') do + Fog::Storage[:rackspace].delete_multiple_objects(nil, ['fogobjecttests2/fog_object', 'fogobjecttests2']).body + end + end + end tests('failure') do @@ -132,6 +157,42 @@ Shindo.tests('Fog::Storage[:rackspace] | object requests', ["rackspace"]) do Fog::Storage[:rackspace].delete_object('fognoncontainer', 'fog_non_object') end + tests('#delete_multiple_objects') do + pending if Fog.mocking? + + expected = { + "Number Not Found" => 2, + "Response Status" => "200 OK", + "Errors" => [], + "Number Deleted" => 0, + "Response Body" => "" + } + + returns(expected, 'reports missing objects') do + Fog::Storage[:rackspace].delete_multiple_objects('fogobjecttests', ['fog_non_object', 'fog_non_object2']).body + end + + returns(expected, 'reports missing container') do + Fog::Storage[:rackspace].delete_multiple_objects('fognoncontainer', ['fog_non_object', 'fog_non_object2']).body + end + + tests('deleting non-empty container') do + Fog::Storage[:rackspace].put_object('fogobjecttests', 'fog_object', lorem_file) + + expected = { + "Number Not Found" => 0, + "Response Status" => "400 Bad Request", + "Errors" => [['fogobjecttests', '409 Conflict']], + "Number Deleted" => 1, + "Response Body" => "" + } + + returns(expected, 'deletes object but not container') do + Fog::Storage[:rackspace].delete_multiple_objects(nil, ['fogobjecttests', 'fogobjecttests/fog_object']).body + end + end + end + end unless Fog.mocking?