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