diff --git a/lib/fog/digitalocean/compute.rb b/lib/fog/digitalocean/compute.rb index b1487ac95..77b77313d 100644 --- a/lib/fog/digitalocean/compute.rb +++ b/lib/fog/digitalocean/compute.rb @@ -90,17 +90,46 @@ module Fog params[:query].merge!(:api_key => @digitalocean_api_key) params[:query].merge!(:client_id => @digitalocean_client_id) - response = @connection.request(params) + response = retry_event_lock { parse @connection.request(params) } unless response.body.empty? - response.body = Fog::JSON.decode(response.body) if response.body['status'] != 'OK' - raise Fog::Errors::Error.new response.body.to_s + case response.body['error_message'] + when /No Droplets Found/ + raise Fog::Errors::NotFound.new + else + raise Fog::Errors::Error.new response.body.to_s + end end end response end + private + + def parse(response) + return response if response.body.empty? + response.body = Fog::JSON.decode(response.body) + response + end + + def retry_event_lock + count = 0 + reponse = nil + while count < 5 + response = yield + + if response.body && response.body['error_message'] =~ /There is already a pending event for the droplet/ + count += 1 + sleep count ** 3 + else + break + end + end + + response + end + end end end diff --git a/lib/fog/digitalocean/models/compute/server.rb b/lib/fog/digitalocean/models/compute/server.rb index 84261a529..bd4cb9b18 100644 --- a/lib/fog/digitalocean/models/compute/server.rb +++ b/lib/fog/digitalocean/models/compute/server.rb @@ -7,13 +7,13 @@ module Fog # A DigitalOcean Droplet # class Server < Fog::Compute::Server - + identity :id attribute :name - attribute :state, :aliases => 'status' + attribute :state, :aliases => 'status' attribute :image_id attribute :region_id - attribute :flavor_id, :aliases => 'size_id' + attribute :flavor_id, :aliases => 'size_id' # Not documented in their API, but # available nevertheless attribute :ip_address @@ -52,7 +52,7 @@ module Fog # Works as a power switch. # The server consumes resources while powered off # so you are still charged. - # + # # @see https://www.digitalocean.com/community/questions/am-i-charged-while-my-droplet-is-in-a-powered-off-state def stop requires :id @@ -64,7 +64,7 @@ module Fog # The server consumes resources while powered on # so you will be charged. # - # Each time a server is spun up, even if for a few seconds, + # Each time a server is spun up, even if for a few seconds, # it is charged for an hour. # def start @@ -85,7 +85,7 @@ module Fog # :image_id => image_id_here, # :flavor_id => flavor_id_here, # :region_id => region_id_here - # + # # @return [Boolean] def save raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if persisted? @@ -93,11 +93,11 @@ module Fog options = {} if attributes[:ssh_key_ids] - options[:ssh_key_ids] = attributes[:ssh_key_ids] + options[:ssh_key_ids] = attributes[:ssh_key_ids] end - data = service.create_server name, - flavor_id, - image_id, + data = service.create_server name, + flavor_id, + image_id, region_id, options merge_attributes(data.body['droplet']) @@ -105,7 +105,7 @@ module Fog end # Destroy the server, freeing up the resources. - # + # # DigitalOcean will stop charging you for the resources # the server was using. # @@ -115,9 +115,9 @@ module Fog # IMPORTANT: As of 2013/01/31, you should wait some time to # destroy the server after creating it. If you try to destroy # the server too fast, the destroy event may be lost and the - # server will remain running and consuming resources, so + # server will remain running and consuming resources, so # DigitalOcean will keep charging you. - # Double checked this with DigitalOcean staff and confirmed + # Double checked this with DigitalOcean staff and confirmed # that it's the way it works right now. # # Double check the server has been destroyed! diff --git a/tests/digitalocean/helper.rb b/tests/digitalocean/helper.rb index 05c0c7297..1edc3d00a 100644 --- a/tests/digitalocean/helper.rb +++ b/tests/digitalocean/helper.rb @@ -4,36 +4,43 @@ def service Fog::Compute[:digitalocean] end +def fog_test_server_attributes + image = service.images.find { |i| i.name == 'Ubuntu 12.04 x64' } + region = service.regions.find { |r| r.name == 'New York 1' } + flavor = service.flavors.find { |r| r.name == '512MB' } + + { + :image_id => image.id, + :region_id => region.id, + :flavor_id => flavor.id + } +end + +def fog_server_name + "fog-server-test" +end + # Create a long lived server for the tests def fog_test_server - server = service.servers.find { |s| s.name == 'fog-test-server' } + server = service.servers.find { |s| s.name == fog_server_name } unless server - image = service.images.find { |i| i.name == 'Ubuntu 12.04 x64 Server' } - region = service.regions.find { |r| r.name == 'New York 1' } - flavor = service.flavors.find { |r| r.name == '512MB' } - server = service.servers.create :name => 'fog-test-server', - :image_id => image.id, - :region_id => region.id, - :flavor_id => flavor.id - # Wait for the server to come up - begin - server.wait_for(120) { server.reload rescue nil; server.ready? } - rescue Fog::Errors::TimeoutError - # Server bootstrap took more than 120 secs! - end + server = service.servers.create({ + :name => fog_server_name + }.merge(fog_test_server_attributes)) + server.wait_for { ready? } end server end # Destroy the long lived server def fog_test_server_destroy - server = service.servers.find { |s| s.name == 'fog-test-server' } + server = service.servers.find { |s| s.name == fog_server_name } server.destroy if server end at_exit do unless Fog.mocking? || Fog.credentials[:digitalocean_api_key].nil? - server = service.servers.find { |s| s.name == 'fog-test-server' } + server = service.servers.find { |s| s.name == fog_server_name } if server server.wait_for(120) do reload rescue nil; ready? diff --git a/tests/digitalocean/models/compute/servers_tests.rb b/tests/digitalocean/models/compute/servers_tests.rb index e84b59593..a4910e7d5 100644 --- a/tests/digitalocean/models/compute/servers_tests.rb +++ b/tests/digitalocean/models/compute/servers_tests.rb @@ -1,35 +1,12 @@ Shindo.tests('Fog::Compute[:digitalocean] | servers collection', ['digitalocean']) do + service = Fog::Compute[:digitalocean] - service = Fog::Compute[:digitalocean] - - tests('The servers collection') do - servers = service.servers.all - - server = fog_test_server - - test('should NOT be empty') do - servers.reload - !servers.empty? - end - - test('should be a kind of Fog::Compute::DigitalOcean::Servers') do - servers.kind_of? Fog::Compute::DigitalOcean::Servers - end - - tests('should have Fog::Compute::DigitalOcean::Servers inside') do - servers.each do |s| - test { s.kind_of? Fog::Compute::DigitalOcean::Server } - end - end - - tests('should be able to reload itself').succeeds { servers.reload } - - tests('should be able to get a model') do - test('by instance id') do - servers.get(server.id).kind_of? Fog::Compute::DigitalOcean::Server - end - end + options = { + :name => "#{fog_server_name}-#{Time.now.to_i.to_s}" + }.merge fog_test_server_attributes + collection_tests(service.servers, options, true) do + @instance.wait_for { ready? } end end diff --git a/tests/digitalocean/requests/compute/create_server_tests.rb b/tests/digitalocean/requests/compute/create_server_tests.rb index 592f0e8fc..024da5c38 100644 --- a/tests/digitalocean/requests/compute/create_server_tests.rb +++ b/tests/digitalocean/requests/compute/create_server_tests.rb @@ -15,7 +15,7 @@ Shindo.tests('Fog::Compute[:digitalocean] | create_server request', ['digitaloce tests('#create_server').formats({'status' => 'OK', 'droplet' => @server_format}) do image = service.images.find { |img| img.name == 'Ubuntu 12.04 x64 Server' } flavor = service.flavors.find { |f| f.name == '512MB' } - data = Fog::Compute[:digitalocean].create_server 'fog-test-server', + data = Fog::Compute[:digitalocean].create_server fog_server_name, flavor.id, image.id, service.regions.first.id diff --git a/tests/digitalocean/requests/compute/get_server_details_tests.rb b/tests/digitalocean/requests/compute/get_server_details_tests.rb index 012d2ce63..527dcbbc6 100644 --- a/tests/digitalocean/requests/compute/get_server_details_tests.rb +++ b/tests/digitalocean/requests/compute/get_server_details_tests.rb @@ -5,7 +5,7 @@ Shindo.tests('Fog::Compute[:digitalocean] | get_server_details request', ['digit test('#get_server_details') do server = fog_test_server body = Fog::Compute[:digitalocean].get_server_details(server.id).body - body['droplet']['name'] == 'fog-test-server' + body['droplet']['name'] == fog_server_name end end diff --git a/tests/helpers/collection_helper.rb b/tests/helpers/collection_helper.rb index b5168a46f..b2544c919 100644 --- a/tests/helpers/collection_helper.rb +++ b/tests/helpers/collection_helper.rb @@ -21,8 +21,8 @@ def collection_tests(collection, params = {}, mocks_implemented = true) pending if Fog.mocking? && !mocks_implemented collection.all end - - + + if !Fog.mocking? || mocks_implemented @identity = @instance.identity @@ -37,7 +37,7 @@ def collection_tests(collection, params = {}, mocks_implemented = true) pending if Fog.mocking? && !mocks_implemented [ - 'all?', 'any?', 'find', 'detect', 'collect', 'map', + 'all?', 'any?', 'find', 'detect', 'collect', 'map', 'find_index', 'flat_map', 'collect_concat', 'group_by', 'none?', 'one?' ].each do |enum_method| @@ -74,7 +74,7 @@ def collection_tests(collection, params = {}, mocks_implemented = true) @instance.destroy end end - + tests('failure') do if !Fog.mocking? || mocks_implemented