mirror of
https://github.com/fog/fog.git
synced 2022-11-09 13:51:43 -05:00
Cleanup and refactor digitalocean integration
* Handle case where droplet is locked pending events, DO support says they are planning to expose something in the api to show these events. Until then this is the best we have. * Whitespace cleanup * Refactor test helpers * Use the collection helper for the digitalocean servers collection
This commit is contained in:
parent
a23d615ad5
commit
dc6e78d6bd
7 changed files with 80 additions and 67 deletions
|
@ -90,17 +90,46 @@ module Fog
|
||||||
params[:query].merge!(:api_key => @digitalocean_api_key)
|
params[:query].merge!(:api_key => @digitalocean_api_key)
|
||||||
params[:query].merge!(:client_id => @digitalocean_client_id)
|
params[:query].merge!(:client_id => @digitalocean_client_id)
|
||||||
|
|
||||||
response = @connection.request(params)
|
response = retry_event_lock { parse @connection.request(params) }
|
||||||
|
|
||||||
unless response.body.empty?
|
unless response.body.empty?
|
||||||
response.body = Fog::JSON.decode(response.body)
|
|
||||||
if response.body['status'] != 'OK'
|
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
|
||||||
end
|
end
|
||||||
response
|
response
|
||||||
end
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,13 +7,13 @@ module Fog
|
||||||
# A DigitalOcean Droplet
|
# A DigitalOcean Droplet
|
||||||
#
|
#
|
||||||
class Server < Fog::Compute::Server
|
class Server < Fog::Compute::Server
|
||||||
|
|
||||||
identity :id
|
identity :id
|
||||||
attribute :name
|
attribute :name
|
||||||
attribute :state, :aliases => 'status'
|
attribute :state, :aliases => 'status'
|
||||||
attribute :image_id
|
attribute :image_id
|
||||||
attribute :region_id
|
attribute :region_id
|
||||||
attribute :flavor_id, :aliases => 'size_id'
|
attribute :flavor_id, :aliases => 'size_id'
|
||||||
# Not documented in their API, but
|
# Not documented in their API, but
|
||||||
# available nevertheless
|
# available nevertheless
|
||||||
attribute :ip_address
|
attribute :ip_address
|
||||||
|
@ -52,7 +52,7 @@ module Fog
|
||||||
# Works as a power switch.
|
# Works as a power switch.
|
||||||
# The server consumes resources while powered off
|
# The server consumes resources while powered off
|
||||||
# so you are still charged.
|
# so you are still charged.
|
||||||
#
|
#
|
||||||
# @see https://www.digitalocean.com/community/questions/am-i-charged-while-my-droplet-is-in-a-powered-off-state
|
# @see https://www.digitalocean.com/community/questions/am-i-charged-while-my-droplet-is-in-a-powered-off-state
|
||||||
def stop
|
def stop
|
||||||
requires :id
|
requires :id
|
||||||
|
@ -64,7 +64,7 @@ module Fog
|
||||||
# The server consumes resources while powered on
|
# The server consumes resources while powered on
|
||||||
# so you will be charged.
|
# 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.
|
# it is charged for an hour.
|
||||||
#
|
#
|
||||||
def start
|
def start
|
||||||
|
@ -85,7 +85,7 @@ module Fog
|
||||||
# :image_id => image_id_here,
|
# :image_id => image_id_here,
|
||||||
# :flavor_id => flavor_id_here,
|
# :flavor_id => flavor_id_here,
|
||||||
# :region_id => region_id_here
|
# :region_id => region_id_here
|
||||||
#
|
#
|
||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
def save
|
def save
|
||||||
raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if persisted?
|
raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if persisted?
|
||||||
|
@ -93,11 +93,11 @@ module Fog
|
||||||
|
|
||||||
options = {}
|
options = {}
|
||||||
if attributes[:ssh_key_ids]
|
if attributes[:ssh_key_ids]
|
||||||
options[:ssh_key_ids] = attributes[:ssh_key_ids]
|
options[:ssh_key_ids] = attributes[:ssh_key_ids]
|
||||||
end
|
end
|
||||||
data = service.create_server name,
|
data = service.create_server name,
|
||||||
flavor_id,
|
flavor_id,
|
||||||
image_id,
|
image_id,
|
||||||
region_id,
|
region_id,
|
||||||
options
|
options
|
||||||
merge_attributes(data.body['droplet'])
|
merge_attributes(data.body['droplet'])
|
||||||
|
@ -105,7 +105,7 @@ module Fog
|
||||||
end
|
end
|
||||||
|
|
||||||
# Destroy the server, freeing up the resources.
|
# Destroy the server, freeing up the resources.
|
||||||
#
|
#
|
||||||
# DigitalOcean will stop charging you for the resources
|
# DigitalOcean will stop charging you for the resources
|
||||||
# the server was using.
|
# the server was using.
|
||||||
#
|
#
|
||||||
|
@ -115,9 +115,9 @@ module Fog
|
||||||
# IMPORTANT: As of 2013/01/31, you should wait some time to
|
# IMPORTANT: As of 2013/01/31, you should wait some time to
|
||||||
# destroy the server after creating it. If you try to destroy
|
# destroy the server after creating it. If you try to destroy
|
||||||
# the server too fast, the destroy event may be lost and the
|
# 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.
|
# 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.
|
# that it's the way it works right now.
|
||||||
#
|
#
|
||||||
# Double check the server has been destroyed!
|
# Double check the server has been destroyed!
|
||||||
|
|
|
@ -4,36 +4,43 @@ def service
|
||||||
Fog::Compute[:digitalocean]
|
Fog::Compute[:digitalocean]
|
||||||
end
|
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
|
# Create a long lived server for the tests
|
||||||
def fog_test_server
|
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
|
unless server
|
||||||
image = service.images.find { |i| i.name == 'Ubuntu 12.04 x64 Server' }
|
server = service.servers.create({
|
||||||
region = service.regions.find { |r| r.name == 'New York 1' }
|
:name => fog_server_name
|
||||||
flavor = service.flavors.find { |r| r.name == '512MB' }
|
}.merge(fog_test_server_attributes))
|
||||||
server = service.servers.create :name => 'fog-test-server',
|
server.wait_for { ready? }
|
||||||
: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
|
|
||||||
end
|
end
|
||||||
server
|
server
|
||||||
end
|
end
|
||||||
|
|
||||||
# Destroy the long lived server
|
# Destroy the long lived server
|
||||||
def fog_test_server_destroy
|
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
|
server.destroy if server
|
||||||
end
|
end
|
||||||
|
|
||||||
at_exit do
|
at_exit do
|
||||||
unless Fog.mocking? || Fog.credentials[:digitalocean_api_key].nil?
|
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
|
if server
|
||||||
server.wait_for(120) do
|
server.wait_for(120) do
|
||||||
reload rescue nil; ready?
|
reload rescue nil; ready?
|
||||||
|
|
|
@ -1,35 +1,12 @@
|
||||||
Shindo.tests('Fog::Compute[:digitalocean] | servers collection', ['digitalocean']) do
|
Shindo.tests('Fog::Compute[:digitalocean] | servers collection', ['digitalocean']) do
|
||||||
|
service = Fog::Compute[:digitalocean]
|
||||||
|
|
||||||
service = Fog::Compute[:digitalocean]
|
options = {
|
||||||
|
:name => "#{fog_server_name}-#{Time.now.to_i.to_s}"
|
||||||
tests('The servers collection') do
|
}.merge fog_test_server_attributes
|
||||||
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
|
|
||||||
|
|
||||||
|
collection_tests(service.servers, options, true) do
|
||||||
|
@instance.wait_for { ready? }
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,7 +15,7 @@ Shindo.tests('Fog::Compute[:digitalocean] | create_server request', ['digitaloce
|
||||||
tests('#create_server').formats({'status' => 'OK', 'droplet' => @server_format}) do
|
tests('#create_server').formats({'status' => 'OK', 'droplet' => @server_format}) do
|
||||||
image = service.images.find { |img| img.name == 'Ubuntu 12.04 x64 Server' }
|
image = service.images.find { |img| img.name == 'Ubuntu 12.04 x64 Server' }
|
||||||
flavor = service.flavors.find { |f| f.name == '512MB' }
|
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,
|
flavor.id,
|
||||||
image.id,
|
image.id,
|
||||||
service.regions.first.id
|
service.regions.first.id
|
||||||
|
|
|
@ -5,7 +5,7 @@ Shindo.tests('Fog::Compute[:digitalocean] | get_server_details request', ['digit
|
||||||
test('#get_server_details') do
|
test('#get_server_details') do
|
||||||
server = fog_test_server
|
server = fog_test_server
|
||||||
body = Fog::Compute[:digitalocean].get_server_details(server.id).body
|
body = Fog::Compute[:digitalocean].get_server_details(server.id).body
|
||||||
body['droplet']['name'] == 'fog-test-server'
|
body['droplet']['name'] == fog_server_name
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,8 +21,8 @@ def collection_tests(collection, params = {}, mocks_implemented = true)
|
||||||
pending if Fog.mocking? && !mocks_implemented
|
pending if Fog.mocking? && !mocks_implemented
|
||||||
collection.all
|
collection.all
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if !Fog.mocking? || mocks_implemented
|
if !Fog.mocking? || mocks_implemented
|
||||||
@identity = @instance.identity
|
@identity = @instance.identity
|
||||||
|
@ -37,7 +37,7 @@ def collection_tests(collection, params = {}, mocks_implemented = true)
|
||||||
pending if Fog.mocking? && !mocks_implemented
|
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',
|
'find_index', 'flat_map', 'collect_concat', 'group_by',
|
||||||
'none?', 'one?'
|
'none?', 'one?'
|
||||||
].each do |enum_method|
|
].each do |enum_method|
|
||||||
|
@ -74,7 +74,7 @@ def collection_tests(collection, params = {}, mocks_implemented = true)
|
||||||
@instance.destroy
|
@instance.destroy
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
tests('failure') do
|
tests('failure') do
|
||||||
|
|
||||||
if !Fog.mocking? || mocks_implemented
|
if !Fog.mocking? || mocks_implemented
|
||||||
|
|
Loading…
Add table
Reference in a new issue