From dc6e78d6bd05a630db35f20eb1fd3fff24553d85 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Wed, 10 Jul 2013 17:41:12 -0700 Subject: [PATCH 1/9] 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 --- lib/fog/digitalocean/compute.rb | 35 +++++++++++++++-- lib/fog/digitalocean/models/compute/server.rb | 26 ++++++------- tests/digitalocean/helper.rb | 39 +++++++++++-------- .../models/compute/servers_tests.rb | 35 +++-------------- .../requests/compute/create_server_tests.rb | 2 +- .../compute/get_server_details_tests.rb | 2 +- tests/helpers/collection_helper.rb | 8 ++-- 7 files changed, 80 insertions(+), 67 deletions(-) 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 From d81d0005464c43ad91daada7162c661acb90fb13 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Thu, 11 Jul 2013 20:48:11 -0700 Subject: [PATCH 2/9] Whitespace --- tests/digitalocean/models/compute/flavor_tests.rb | 4 ++-- tests/digitalocean/models/compute/image_tests.rb | 4 ++-- tests/digitalocean/models/compute/region_tests.rb | 4 ++-- tests/digitalocean/models/compute/ssh_key_tests.rb | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/digitalocean/models/compute/flavor_tests.rb b/tests/digitalocean/models/compute/flavor_tests.rb index f8993cda6..4c2927681 100644 --- a/tests/digitalocean/models/compute/flavor_tests.rb +++ b/tests/digitalocean/models/compute/flavor_tests.rb @@ -2,14 +2,14 @@ Shindo.tests("Fog::Compute[:digitalocean] | flavor model", ['digitalocean', 'com service = Fog::Compute[:digitalocean] flavor = service.flavors.first - + tests('The flavor model should') do tests('have the action') do test('reload') { flavor.respond_to? 'reload' } end tests('have attributes') do model_attribute_hash = flavor.attributes - attributes = [ + attributes = [ :id, :name, ] diff --git a/tests/digitalocean/models/compute/image_tests.rb b/tests/digitalocean/models/compute/image_tests.rb index c189d230d..8707cd9bd 100644 --- a/tests/digitalocean/models/compute/image_tests.rb +++ b/tests/digitalocean/models/compute/image_tests.rb @@ -2,14 +2,14 @@ Shindo.tests("Fog::Compute[:digitalocean] | image model", ['digitalocean', 'comp service = Fog::Compute[:digitalocean] image = service.images.first - + tests('The image model should') do tests('have the action') do test('reload') { image.respond_to? 'reload' } end tests('have attributes') do model_attribute_hash = image.attributes - attributes = [ + attributes = [ :id, :name, :distribution diff --git a/tests/digitalocean/models/compute/region_tests.rb b/tests/digitalocean/models/compute/region_tests.rb index 03345ac67..1a9dc0c37 100644 --- a/tests/digitalocean/models/compute/region_tests.rb +++ b/tests/digitalocean/models/compute/region_tests.rb @@ -2,14 +2,14 @@ Shindo.tests("Fog::Compute[:digitalocean] | region model", ['digitalocean', 'com service = Fog::Compute[:digitalocean] region = service.regions.first - + tests('The region model should') do tests('have the action') do test('reload') { region.respond_to? 'reload' } end tests('have attributes') do model_attribute_hash = region.attributes - attributes = [ + attributes = [ :id, :name, ] diff --git a/tests/digitalocean/models/compute/ssh_key_tests.rb b/tests/digitalocean/models/compute/ssh_key_tests.rb index 906f0a836..531774ad6 100644 --- a/tests/digitalocean/models/compute/ssh_key_tests.rb +++ b/tests/digitalocean/models/compute/ssh_key_tests.rb @@ -3,7 +3,7 @@ Shindo.tests("Fog::Compute[:digitalocean] | ssh_key model", ['digitalocean', 'co service = Fog::Compute[:digitalocean] tests('The ssh_key model should') do - + test('#save') do @key = service.ssh_keys.create :name => 'fookey', :ssh_pub_key => 'fookey' @@ -11,7 +11,7 @@ Shindo.tests("Fog::Compute[:digitalocean] | ssh_key model", ['digitalocean', 'co end tests('have the action') do test('reload') { @key.respond_to? 'reload' } - %w{ + %w{ save destroy }.each do |action| @@ -19,7 +19,7 @@ Shindo.tests("Fog::Compute[:digitalocean] | ssh_key model", ['digitalocean', 'co end end tests('have attributes') do - attributes = [ + attributes = [ :id, :name, :ssh_pub_key From 8dee42d23bc6d3b24890f6c409aa44bf592aaa41 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Fri, 12 Jul 2013 07:22:08 -0700 Subject: [PATCH 3/9] Whitespace, fix tests for running in non mock mode, and clean up in non mock mode --- lib/fog/digitalocean/requests/compute/list_images.rb | 12 ++++++------ tests/digitalocean/models/compute/ssh_key_tests.rb | 4 ++++ tests/digitalocean/models/compute/ssh_keys_tests.rb | 2 ++ .../requests/compute/create_server_tests.rb | 7 ++----- .../requests/compute/destroy_server_tests.rb | 2 -- .../requests/compute/get_ssh_key_tests.rb | 5 ++++- .../requests/compute/list_ssh_keys_tests.rb | 7 +++++-- 7 files changed, 23 insertions(+), 16 deletions(-) diff --git a/lib/fog/digitalocean/requests/compute/list_images.rb b/lib/fog/digitalocean/requests/compute/list_images.rb index e79a9567e..771e88628 100644 --- a/lib/fog/digitalocean/requests/compute/list_images.rb +++ b/lib/fog/digitalocean/requests/compute/list_images.rb @@ -1,6 +1,6 @@ module Fog module Compute - class DigitalOcean + class DigitalOcean class Real def list_images(options = {}) @@ -23,18 +23,18 @@ module Fog "images" => [ # Sample image { - "id" => 1601, - "name" => "CentOS 5.8 x64", + "id" => 1601, + "name" => "CentOS 5.8 x64", "distribution" => "CentOS" }, { - "id" => 1602, - "name" => "CentOS 5.8 x32", + "id" => 1602, + "name" => "CentOS 5.8 x32", "distribution" => "CentOS" }, { "id" => 2676, - "name" => "Ubuntu 12.04 x64 Server", + "name" => "Ubuntu 12.04 x64", "distribution" => "Ubuntu" }, diff --git a/tests/digitalocean/models/compute/ssh_key_tests.rb b/tests/digitalocean/models/compute/ssh_key_tests.rb index 531774ad6..c2753f596 100644 --- a/tests/digitalocean/models/compute/ssh_key_tests.rb +++ b/tests/digitalocean/models/compute/ssh_key_tests.rb @@ -9,6 +9,7 @@ Shindo.tests("Fog::Compute[:digitalocean] | ssh_key model", ['digitalocean', 'co :ssh_pub_key => 'fookey' @key.is_a? Fog::Compute::DigitalOcean::SshKey end + tests('have the action') do test('reload') { @key.respond_to? 'reload' } %w{ @@ -18,18 +19,21 @@ Shindo.tests("Fog::Compute[:digitalocean] | ssh_key model", ['digitalocean', 'co test(action) { @key.respond_to? action } end end + tests('have attributes') do attributes = [ :id, :name, :ssh_pub_key ] + tests("The key model should respond to") do attributes.each do |attribute| test("#{attribute}") { @key.respond_to? attribute } end end end + test('#destroy') do @key.destroy end diff --git a/tests/digitalocean/models/compute/ssh_keys_tests.rb b/tests/digitalocean/models/compute/ssh_keys_tests.rb index 10f64f12a..b6674173f 100644 --- a/tests/digitalocean/models/compute/ssh_keys_tests.rb +++ b/tests/digitalocean/models/compute/ssh_keys_tests.rb @@ -23,6 +23,8 @@ Shindo.tests('Fog::Compute[:digitalocean] | ssh_keys collection', ['digitalocean end end + key.destroy + end end diff --git a/tests/digitalocean/requests/compute/create_server_tests.rb b/tests/digitalocean/requests/compute/create_server_tests.rb index 024da5c38..624cb5e1b 100644 --- a/tests/digitalocean/requests/compute/create_server_tests.rb +++ b/tests/digitalocean/requests/compute/create_server_tests.rb @@ -5,7 +5,7 @@ Shindo.tests('Fog::Compute[:digitalocean] | create_server request', ['digitaloce 'name' => String, 'image_id' => Integer, 'size_id' => Integer, - 'event_id' => Integer + 'event_id' => Integer } service = Fog::Compute[:digitalocean] @@ -13,7 +13,7 @@ Shindo.tests('Fog::Compute[:digitalocean] | create_server request', ['digitaloce tests('success') 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' } flavor = service.flavors.find { |f| f.name == '512MB' } data = Fog::Compute[:digitalocean].create_server fog_server_name, flavor.id, @@ -21,8 +21,5 @@ Shindo.tests('Fog::Compute[:digitalocean] | create_server request', ['digitaloce service.regions.first.id data.body end - end - - end diff --git a/tests/digitalocean/requests/compute/destroy_server_tests.rb b/tests/digitalocean/requests/compute/destroy_server_tests.rb index b1cc72359..d1c312064 100644 --- a/tests/digitalocean/requests/compute/destroy_server_tests.rb +++ b/tests/digitalocean/requests/compute/destroy_server_tests.rb @@ -10,6 +10,4 @@ Shindo.tests('Fog::Compute[:digitalocean] | destroy_server request', ['digitaloc end end - - end diff --git a/tests/digitalocean/requests/compute/get_ssh_key_tests.rb b/tests/digitalocean/requests/compute/get_ssh_key_tests.rb index 88fff114f..56c5c3116 100644 --- a/tests/digitalocean/requests/compute/get_ssh_key_tests.rb +++ b/tests/digitalocean/requests/compute/get_ssh_key_tests.rb @@ -5,16 +5,19 @@ Shindo.tests('Fog::Compute[:digitalocean] | get_ssh_keys request', ['digitalocea 'name' => String, 'ssh_pub_key' => String, } - + service = Fog::Compute[:digitalocean] tests('success') do tests('#get_ssh_key') do key = service.create_ssh_key 'fookey', 'ssh-dss FOO' + tests('format').data_matches_schema(@ssh_key_format) do service.get_ssh_key(key.body['ssh_key']['id']).body['ssh_key'] end + + service.destroy_ssh_key(key.body['ssh_key']['id']) end end diff --git a/tests/digitalocean/requests/compute/list_ssh_keys_tests.rb b/tests/digitalocean/requests/compute/list_ssh_keys_tests.rb index db1595bb2..4d7ce9f3a 100644 --- a/tests/digitalocean/requests/compute/list_ssh_keys_tests.rb +++ b/tests/digitalocean/requests/compute/list_ssh_keys_tests.rb @@ -7,15 +7,18 @@ Shindo.tests('Fog::Compute[:digitalocean] | list_ssh_keys request', ['digitaloce tests('success') do + key = service.create_ssh_key 'fookey', 'ssh-dss FOO' + tests('#list_ssh_keys') do - Fog::Compute[:digitalocean].create_ssh_key 'fookey', 'ssh-dss FOO' - Fog::Compute[:digitalocean].list_ssh_keys.body['ssh_keys'].each do |key| + service.list_ssh_keys.body['ssh_keys'].each do |key| tests('format').data_matches_schema(@ssh_key_format) do key end end end + service.destroy_ssh_key(key.body['ssh_key']['id']) + end end From e47f95b5f96804187b312de71f2ea2de3f540fe2 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Sun, 21 Jul 2013 11:09:53 -0700 Subject: [PATCH 4/9] digitalocean supports bootstrap --- .gitignore | 1 + lib/fog/digitalocean/models/compute/server.rb | 25 ++++++++++++++- .../digitalocean/models/compute/servers.rb | 31 ++++++++++++++++--- .../digitalocean/models/compute/ssh_keys.rb | 5 ++- .../requests/compute/create_server.rb | 15 ++++----- .../models/compute/server_tests.rb | 12 +++++-- .../models/compute/servers_tests.rb | 9 ++++++ .../models/compute/ssh_keys_tests.rb | 4 ++- 8 files changed, 84 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index eab4d501e..ef087879f 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ pkg spec/credentials.yml vendor/* tags +tests/digitalocean/fixtures/ diff --git a/lib/fog/digitalocean/models/compute/server.rb b/lib/fog/digitalocean/models/compute/server.rb index bd4cb9b18..1db59347a 100644 --- a/lib/fog/digitalocean/models/compute/server.rb +++ b/lib/fog/digitalocean/models/compute/server.rb @@ -16,9 +16,11 @@ module Fog attribute :flavor_id, :aliases => 'size_id' # Not documented in their API, but # available nevertheless - attribute :ip_address + attribute :public_ip_address, :aliases => 'ip_address' attribute :backups_active + attr_writer :ssh_keys + # Reboot the server (soft reboot). # # The preferred method of rebooting a server. @@ -72,6 +74,25 @@ module Fog service.power_on_server self.id end + def setup(credentials = {}) + requires :public_ip_address + require 'net/ssh' + + commands = [ + %{mkdir .ssh}, + %{passwd -l #{username}}, + %{echo "#{Fog::JSON.encode(Fog::JSON.sanitize(attributes))}" >> ~/attributes.json} + ] + if public_key + commands << %{echo "#{public_key}" >> ~/.ssh/authorized_keys} + end + + # wait for aws to be ready + wait_for { sshable?(credentials) } + + Fog::SSH.new(public_ip_address, username, credentials).run(commands) + end + # Creates the server (not to be called directly). # # Usually called by Fog::Collection#create @@ -94,6 +115,8 @@ module Fog options = {} if attributes[:ssh_key_ids] options[:ssh_key_ids] = attributes[:ssh_key_ids] + elsif @ssh_keys + options[:ssh_key_ids] = @ssh_keys.map(&:id) end data = service.create_server name, flavor_id, diff --git a/lib/fog/digitalocean/models/compute/servers.rb b/lib/fog/digitalocean/models/compute/servers.rb index 08f6de6d7..51a5eeaa5 100644 --- a/lib/fog/digitalocean/models/compute/servers.rb +++ b/lib/fog/digitalocean/models/compute/servers.rb @@ -6,16 +6,39 @@ module Fog class DigitalOcean class Servers < Fog::Collection + model Fog::Compute::DigitalOcean::Server def all(filters = {}) - load service.list_servers.body['droplets'] + data = service.list_servers.body['droplets'] + load(data) + end + + def bootstrap(new_attributes = {}) + server = new(new_attributes) + + raise(ArgumentError, "public_key_path is required to configure the server.") unless new_attributes[:public_key_path] + raise(ArgumentError, "private_key_path is required to configure the server.") unless new_attributes[:private_key_path] + credential = Fog.respond_to?(:credential) && Fog.credential || :default + name = "fog_#{credential}" + ssh_key = service.ssh_keys.detect { |key| key.name == name } + if ssh_key.nil? + ssh_key = service.ssh_keys.create( + :name => name, + :ssh_pub_key => File.read(new_attributes[:public_key_path]) + ) + end + server.ssh_keys = [ssh_key] + + server.save + server.wait_for { ready? } + server.setup :keys => [new_attributes[:private_key_path]] + server end def get(id) - if server = service.get_server_details(id).body['droplet'] - new server - end + server = service.get_server_details(id).body['droplet'] + new(server) if server rescue Fog::Errors::NotFound nil end diff --git a/lib/fog/digitalocean/models/compute/ssh_keys.rb b/lib/fog/digitalocean/models/compute/ssh_keys.rb index 6e5d27bc6..a7f1ef4df 100644 --- a/lib/fog/digitalocean/models/compute/ssh_keys.rb +++ b/lib/fog/digitalocean/models/compute/ssh_keys.rb @@ -15,9 +15,8 @@ module Fog end def get(uri) - if data = service.get_ssh_key(uri) - new(data.body) - end + data = service.get_ssh_key(uri).body['ssh_key'] + new(data) rescue Fog::Errors::NotFound nil end diff --git a/lib/fog/digitalocean/requests/compute/create_server.rb b/lib/fog/digitalocean/requests/compute/create_server.rb index 025bc91eb..9de6449ac 100644 --- a/lib/fog/digitalocean/requests/compute/create_server.rb +++ b/lib/fog/digitalocean/requests/compute/create_server.rb @@ -6,9 +6,9 @@ module Fog # # FIXME: missing ssh keys support # - def create_server( name, - size_id, - image_id, + def create_server( name, + size_id, + image_id, region_id, options = {} ) @@ -20,9 +20,10 @@ module Fog } if options[:ssh_key_ids] + options[:ssh_key_ids] = options[:ssh_key_ids].join(",") if options[:ssh_key_ids].is_a? Array query_hash[:ssh_key_ids] = options[:ssh_key_ids] end - + request( :expects => [200], :method => 'GET', @@ -35,9 +36,9 @@ module Fog class Mock - def create_server( name, - size_id, - image_id, + def create_server( name, + size_id, + image_id, region_id, options = {} ) response = Excon::Response.new diff --git a/tests/digitalocean/models/compute/server_tests.rb b/tests/digitalocean/models/compute/server_tests.rb index e62337ef8..0a37780f2 100644 --- a/tests/digitalocean/models/compute/server_tests.rb +++ b/tests/digitalocean/models/compute/server_tests.rb @@ -16,6 +16,7 @@ Shindo.tests("Fog::Compute[:digitalocean] | server model", ['digitalocean', 'com test(action) { server.respond_to? action } end end + tests('have attributes') do model_attribute_hash = server.attributes attributes = [ @@ -26,7 +27,8 @@ Shindo.tests("Fog::Compute[:digitalocean] | server model", ['digitalocean', 'com :ip_address, :flavor_id, :region_id, - :image_id + :image_id, + :ssh_keys= ] tests("The server model should respond to") do attributes.each do |attribute| @@ -34,12 +36,14 @@ Shindo.tests("Fog::Compute[:digitalocean] | server model", ['digitalocean', 'com end end end + test('#reboot') do pending if Fog.mocking? server.reboot server.wait_for { server.state == 'off' } server.state == 'off' end + test('#power_cycle') do pending if Fog.mocking? server.wait_for { server.ready? } @@ -47,16 +51,19 @@ Shindo.tests("Fog::Compute[:digitalocean] | server model", ['digitalocean', 'com server.wait_for { server.state == 'off' } server.state == 'off' end + test('#stop') do server.stop server.wait_for { server.state == 'off' } server.state == 'off' end + test('#start') do server.start server.wait_for { ready? } server.ready? end + # DigitalOcean shutdown is unreliable # so disable it in real mode for now test('#shutdown') do @@ -67,6 +74,7 @@ Shindo.tests("Fog::Compute[:digitalocean] | server model", ['digitalocean', 'com server.wait_for { server.state == 'off' } server.state == 'off' end + test('#update') do begin server.update @@ -74,11 +82,11 @@ Shindo.tests("Fog::Compute[:digitalocean] | server model", ['digitalocean', 'com true end end - end # restore server state server.start + server.wait_for { ready? } end diff --git a/tests/digitalocean/models/compute/servers_tests.rb b/tests/digitalocean/models/compute/servers_tests.rb index a4910e7d5..e67f0f2d7 100644 --- a/tests/digitalocean/models/compute/servers_tests.rb +++ b/tests/digitalocean/models/compute/servers_tests.rb @@ -9,4 +9,13 @@ Shindo.tests('Fog::Compute[:digitalocean] | servers collection', ['digitalocean' @instance.wait_for { ready? } end + tests("#bootstrap").succeeds do + pending if Fog.mocking? + @server = service.servers.bootstrap({ + :public_key_path => File.join(File.dirname(__FILE__), '../../fixtures/id_rsa.pub'), + :private_key_path => File.join(File.dirname(__FILE__), '../../fixtures/id_rsa') + }.merge(options)) + end + + @server.destroy if @server end diff --git a/tests/digitalocean/models/compute/ssh_keys_tests.rb b/tests/digitalocean/models/compute/ssh_keys_tests.rb index b6674173f..ae3c7a352 100644 --- a/tests/digitalocean/models/compute/ssh_keys_tests.rb +++ b/tests/digitalocean/models/compute/ssh_keys_tests.rb @@ -19,7 +19,9 @@ Shindo.tests('Fog::Compute[:digitalocean] | ssh_keys collection', ['digitalocean tests('should be able to get a model') do test('by instance id') do - service.ssh_keys.get(key.id).kind_of? Fog::Compute::DigitalOcean::SshKey + retrieved_key = service.ssh_keys.get(key.id) + test { retrieved_key.kind_of? Fog::Compute::DigitalOcean::SshKey } + test { retrieved_key.name == key.name } end end From 69af9f62e9dade1ff48cc930bf0b6228558ce201 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Mon, 22 Jul 2013 08:05:30 -0700 Subject: [PATCH 5/9] Fix digitalocean server test --- tests/digitalocean/models/compute/server_tests.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/digitalocean/models/compute/server_tests.rb b/tests/digitalocean/models/compute/server_tests.rb index 0a37780f2..c374ab0f4 100644 --- a/tests/digitalocean/models/compute/server_tests.rb +++ b/tests/digitalocean/models/compute/server_tests.rb @@ -24,7 +24,7 @@ Shindo.tests("Fog::Compute[:digitalocean] | server model", ['digitalocean', 'com :name, :state, :backups_active, - :ip_address, + :public_ip_address, :flavor_id, :region_id, :image_id, From f240381e3192c732a85a123d981db3b59e03ef9c Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Mon, 22 Jul 2013 09:11:42 -0700 Subject: [PATCH 6/9] Add digitalocean mock key to run these tests on travis --- tests/helpers/mock_helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/helpers/mock_helper.rb b/tests/helpers/mock_helper.rb index 285f32e67..68c159e15 100644 --- a/tests/helpers/mock_helper.rb +++ b/tests/helpers/mock_helper.rb @@ -28,6 +28,8 @@ if Fog.mock? :cloudstack_zone_id => 'c554c592-e09c-9df5-7688-4a32754a4305', :clodo_api_key => 'clodo_api_key', :clodo_username => 'clodo_username', + :digitalocean_api_key => 'digitalocean_api_key', + :digitalocean_client_id => 'digitalocean_client_id', :dnsimple_email => 'dnsimple_email', :dnsimple_password => 'dnsimple_password', :dnsmadeeasy_api_key => 'dnsmadeeasy_api_key', From 917d4695176d435c6e0e1a7ea5ecc93d2325c8c6 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Tue, 23 Jul 2013 22:04:50 -0700 Subject: [PATCH 7/9] Add ip_address to the mock --- lib/fog/digitalocean/requests/compute/create_server.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/fog/digitalocean/requests/compute/create_server.rb b/lib/fog/digitalocean/requests/compute/create_server.rb index 9de6449ac..8c0b202ba 100644 --- a/lib/fog/digitalocean/requests/compute/create_server.rb +++ b/lib/fog/digitalocean/requests/compute/create_server.rb @@ -51,6 +51,7 @@ module Fog "size_id" => size_id, "image_id" => image_id, "region_id" => region_id, + "ip_address" => "127.0.0.1", "status" => 'active' } From cc483e1bba22c54e18215a74107d4bde77627f58 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Wed, 24 Jul 2013 07:23:06 -0700 Subject: [PATCH 8/9] Remove mock server from servers list on destroy --- lib/fog/digitalocean/requests/compute/destroy_server.rb | 3 +++ tests/digitalocean/requests/compute/destroy_server_tests.rb | 1 + 2 files changed, 4 insertions(+) diff --git a/lib/fog/digitalocean/requests/compute/destroy_server.rb b/lib/fog/digitalocean/requests/compute/destroy_server.rb index e672495f6..26f670ef6 100644 --- a/lib/fog/digitalocean/requests/compute/destroy_server.rb +++ b/lib/fog/digitalocean/requests/compute/destroy_server.rb @@ -25,6 +25,9 @@ module Fog "event_id" => Fog::Mock.random_numbers(1).to_i, "status" => "OK" } + + server = self.data[:servers].reject! { |s| s['id'] == id } + response end diff --git a/tests/digitalocean/requests/compute/destroy_server_tests.rb b/tests/digitalocean/requests/compute/destroy_server_tests.rb index d1c312064..42830dfe9 100644 --- a/tests/digitalocean/requests/compute/destroy_server_tests.rb +++ b/tests/digitalocean/requests/compute/destroy_server_tests.rb @@ -7,6 +7,7 @@ Shindo.tests('Fog::Compute[:digitalocean] | destroy_server request', ['digitaloc test('#destroy_server') do service.destroy_server(server.id).body['status'] == 'OK' + service.servers.get(server.id) == nil end end From dbdf1e1f247056383ef098fb4ff534dae8da4b46 Mon Sep 17 00:00:00 2001 From: Trae Robrock Date: Mon, 29 Jul 2013 10:10:25 -0700 Subject: [PATCH 9/9] Add ip_address method back, but deprecate it. --- lib/fog/digitalocean/models/compute/server.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/fog/digitalocean/models/compute/server.rb b/lib/fog/digitalocean/models/compute/server.rb index 1db59347a..534ee1ea0 100644 --- a/lib/fog/digitalocean/models/compute/server.rb +++ b/lib/fog/digitalocean/models/compute/server.rb @@ -21,6 +21,12 @@ module Fog attr_writer :ssh_keys + # Deprecated: Use public_ip_address instead. + def ip_address + Fog::Logger.warning("ip_address has been deprecated. Use public_ip_address instead") + public_ip_address + end + # Reboot the server (soft reboot). # # The preferred method of rebooting a server.