1
0
Fork 0
mirror of https://github.com/fog/fog.git synced 2022-11-09 13:51:43 -05:00

support DigitalOcean v2 compute API

This commit adds support for the DigitalOcean v2 API. It implements all the compute actions.
This commit is contained in:
bryanl 2015-09-23 14:28:57 -04:00
parent a190c4f3bd
commit b1dc264a56
46 changed files with 2095 additions and 0 deletions

View file

@ -0,0 +1,108 @@
require 'fog/digitalocean/core'
module Fog
module Compute
class DigitalOceanV2 < Fog::Service
requires :digitalocean_token
model_path 'fog/digitalocean/models/compute_v2'
model :server
collection :servers
model :image
collection :images
model :region
collection :regions
model :flavor
collection :flavors
model :ssh_key
collection :ssh_keys
request_path 'fog/digitalocean/requests/compute_v2'
request :change_kernel
request :create_server
request :create_ssh_key
request :delete_server
request :delete_ssh_key
request :disable_backups
request :enable_ipv6
request :enable_private_networking
request :get_droplet_action
request :get_server_details
request :get_ssh_key
request :list_droplet_actions
request :list_flavors
request :list_images
request :list_regions
request :list_servers
request :list_ssh_keys
request :password_reset
request :power_cycle
request :power_off
request :power_on
request :reboot_server
request :rebuild
request :rename
request :resize
request :restore
request :shutdown
request :snapshot
request :update_ssh_key
request :upgrade
class Mock
def self.data
@data ||= Hash.new do |hash, key|
hash[key] = {
:servers => [],
:ssh_keys => []
}
end
end
def initialize(options={})
@digitalocean_token = options[:digitalocean_token]
end
def data
self.class.data[@digitalocean_token]
end
def reset_data
self.class.data.delete(@digitalocean_token)
end
end
class Real
def initialize(options={})
digitalocean_token = options[:digitalocean_token]
persistent = false
options = {
headers: {
'Authorization' => "Bearer #{digitalocean_token}",
}
}
@connection = Fog::Core::Connection.new 'https://api.digitalocean.com', persistent, options
end
def request(params)
params[:headers] ||= {}
begin
response = @connection.request(params)
rescue Excon::Errors::HTTPStatusError => error
raise case error
when Excon::Errors::NotFound
Fog::Compute::Bluebox::NotFound.slurp(error)
else
error
end
end
unless response.body.empty?
response.body = Fog::JSON.decode(response.body)
end
response
end
end
end
end
end

View file

@ -0,0 +1,17 @@
module Fog
module Compute
class DigitalOceanV2
class Flavor < Fog::Model
identity :slug
attribute :available
attribute :transfer
attribute :price_monthly
attribute :price_hourly
attribute :memory
attribute :vcpus
attribute :disk
attribute :regions
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Compute
class DigitalOceanV2
class Flavors < Fog::Collection
model Fog::Compute::DigitalOceanV2::Flavor
def all(filters = {})
data = service.list_flavors.body["sizes"]
load(data)
end
end
end
end
end

View file

@ -0,0 +1,17 @@
module Fog
module Compute
class DigitalOceanV2
class Image < Fog::Model
identity :id
attribute :name
attribute :type
attribute :distribution
attribute :slug
attribute :public
attribute :regions
attribute :min_disk_size
attribute :created_at
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Compute
class DigitalOceanV2
class Images < Fog::Collection
model Fog::Compute::DigitalOceanV2::Image
def all(filters = {})
data = service.list_images.body["images"]
load(data)
end
end
end
end
end

View file

@ -0,0 +1,13 @@
module Fog
module Compute
class DigitalOceanV2
class Region < Fog::Model
identity :slug
attribute :name
attribute :sizes
attribute :available
attribute :features
end
end
end
end

View file

@ -0,0 +1,14 @@
module Fog
module Compute
class DigitalOceanV2
class Regions < Fog::Collection
model Fog::Compute::DigitalOceanV2::Region
def all(filters = {})
data = service.list_regions.body["regions"]
load(data)
end
end
end
end
end

View file

@ -0,0 +1,156 @@
require 'fog/compute/models/server'
module Fog
module Compute
class DigitalOceanV2
# A DigitalOcean Droplet
#
class Server < Fog::Compute::Server
identity :id
attribute :name
attribute :memory
attribute :vcpus
attribute :disk
attribute :locked
attribute :created_at
attribute :status
attribute :backup_ids
attribute :snapshot_ids
attribute :features
attribute :region
attribute :image
attribute :size
attribute :size_slug
attribute :networks
attribute :kernel
attribute :next_backup_window
attribute :private_networking
attribute :backups
attribute :ipv6
attribute :ssh_keys
def public_ip_address
if (pub_net = networks['v4'].find { |n| n['type'] == 'public' })
pub_net['ip_address']
end
end
def save
raise Fog::Errors::Error.new('Re-saving an existing object may create a duplicate') if persisted?
requires :name, :region, :size, :image
options = {}
if attributes[:ssh_keys]
options[:ssh_keys] = attributes[:ssh_keys]
elsif @ssh_keys
options[:ssh_keys] = @ssh_keys.map(&:id)
end
options[:private_networking] = private_networking
options[:backups] = backups
options[:ipv6] = ipv6
data = service.create_server(name, size, image, region, options)
merge_attributes(data.body['droplet'])
true
end
def delete
requires :id
response = service.delete_server id
response.body
end
def ready?
status == 'active'
end
def actions
requires :id
response = service.list_droplet_actions id
response.body
end
def action(action_id)
requires :id
response = service.get_droplet_action(id, action_id)
response.body
end
def reboot
perform_action :reboot_server
end
def disable_backups
perform_action :disable_backups
end
def power_cycle
perform_action :power_cycle
end
def shutdown
perform_action :shutdown
end
def power_off
perform_action :power_off
end
def power_on
perform_action :power_on
end
def restore(image)
perform_action :restore, image
end
def password_reset
perform_action :password_reset
end
def resize(resize_disk, size)
perform_action :resize, resize_disk, size
end
def rebuild(image)
perform_action :rebuild, image
end
def rename(name)
perform_action :rename, name
end
def change_kernel(kernel)
perform_action :change_kernel, kernel
end
def enable_ipv6
perform_action :enable_ipv6
end
def enable_private_networking
perform_action :enable_private_networking
end
def snapshot(name)
perform_action :snapshot, name
end
def upgrade
perform_action :upgrade
end
private
# Performs a droplet action with the given set of arguments.
def perform_action(action, *args)
requires :id
response = service.send(action, id, *args)
response.body
end
end
end
end
end

View file

@ -0,0 +1,26 @@
require 'fog/core/collection'
require 'fog/digitalocean/models/compute/server'
module Fog
module Compute
class DigitalOceanV2
class Servers < Fog::Collection
model Fog::Compute::DigitalOceanV2::Server
# Returns list of servers
def all(filters = {})
data = service.list_servers.body['droplets']
load(data)
end
# Retrieves server
def get(id)
server = service.get_server_details(id).body['droplet']
new(server) if server
rescue Fog::Errors::NotFound
nil
end
end
end
end
end

View file

@ -0,0 +1,30 @@
module Fog
module Compute
class DigitalOceanV2
class SshKey < Fog::Model
identity :id
attribute :fingerprint
attribute :public_key
attribute :name
end
def save
requires :name, :public_key
merge_attributes(service.create_ssh_key(name, public_key).body['ssh_key'])
true
end
def destroy
requires :id
service.delete_ssh_key id
end
def update
requires :id, :name
data = service.update_server(id, name)
merge_attributes(data.body['ssh_key'])
true
end
end
end
end

View file

@ -0,0 +1,21 @@
module Fog
module Compute
class DigitalOceanV2
class SshKeys < Fog::Collection
model Fog::Compute::DigitalOceanV2::SshKey
def all(filters={})
data = service.list_ssh_keys.body['ssh_keys']
load(data)
end
def get(id)
key = service.get_ssh_key(id).body['ssh_key']
new(key) if key
rescue Fog::Errors::NotFound
nil
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def change_kernel(id, kernel)
body = { :type => "change_kernel", :kernel => kernel }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def change_kernel(id, kernel)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "change_kernel",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,93 @@
module Fog
module Compute
class DigitalOceanV2
# noinspection RubyStringKeysInHashInspection
class Real
def create_server(name,
size,
image,
region,
options = {})
create_options = {
:name => name,
:region => region,
:size => size,
:image => image,
}
[:backups, :ipv6, :private_networking].each do |opt|
create_options[opt] = !!options[opt] if options[opt]
end
[:user_data, :ssh_keys].each do |opt|
create_options[opt] = options[opt] if options[opt]
end
encoded_body = Fog::JSON.encode(create_options)
request(
:expects => [202],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => '/v2/droplets',
:body => encoded_body,
)
end
end
# noinspection RubyStringKeysInHashInspection
class Mock
def create_server(name,
size,
image,
region,
options = {})
response = Excon::Response.new
response.status = 202
response.body = {
'droplet' => {
'id' => 3164494,
'name' => name,
'memory' => 512,
'vcpus' => 1,
'disk' => 20,
'locked' => true,
'status' => 'new',
'kernel' => {
'id' => 2233,
'name' => 'Ubuntu 14.04 x64 vmlinuz-3.13.0-37-generic',
'version' => '3.13.0-37-generic'
},
'created_at' => '2014-11-14T16:36:31Z',
'features' => %w(virtio),
'backup_ids' => [],
'snapshot_ids' => [],
'image' => {},
'size' => {},
'size_slug' => '512mb',
'networks' => {},
'region' => {}
},
'links' => {
'actions' => [
{
'id' => 36805096,
'rel' => "create",
'href' => "https://api.digitalocean.com/v2/actions/36805096"
}
]
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,47 @@
module Fog
module Compute
class DigitalOceanV2
# noinspection RubyStringKeysInHashInspection
class Real
def create_ssh_key(name, public_key)
create_options = {
:name => name,
:public_key => public_key,
}
encoded_body = Fog::JSON.encode(create_options)
request(
:expects => [202],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => '/v2/account/keys',
:body => encoded_body,
)
end
end
# noinspection RubyStringKeysInHashInspection
class Mock
def create_server(name, public_key)
response = Excon::Response.new
response.status = 202
response.body ={
'ssh_key' => {
'id' => 512190,
'fingerprint' => "3b:16:bf:e4:8b:00:8b:b8:59:8c:a9:d3:f0:19:45:fa",
'public_key' => "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQDDHr/jh2Jy4yALcK4JyWbVkPRaWmhck3IgCoeOO3z1e2dBowLh64QAM+Qb72pxekALga2oi4GvT+TlWNhzPH4V example",
'name' => "My SSH Public Key"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,26 @@
module Fog
module Compute
class DigitalOceanV2
# noinspection RubyStringKeysInHashInspection
class Real
def delete_server(server_id)
request(
:expects => [204],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'DELETE',
:path => "/v2/droplets/#{server_id}",
)
end
end
# noinspection RubyStringKeysInHashInspection
class Mock
def delete_server(_)
response(:status => 204)
end
end
end
end
end

View file

@ -0,0 +1,26 @@
module Fog
module Compute
class DigitalOceanV2
# noinspection RubyStringKeysInHashInspection
class Real
def delete_ssh_key(id)
request(
:expects => [204],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'DELETE',
:path => "/v2/account/keys#{id}",
)
end
end
# noinspection RubyStringKeysInHashInspection
class Mock
def delete_ssh_key(_)
response(:status => 204)
end
end
end
end
end

View file

@ -0,0 +1,45 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def disable_backups(id)
body = { :type => "disable_backups" }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def disable_backups(id)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "disable_backups",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def enable_ipv6(id)
body = { :type => "enable_ipv6" }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def enable_ipv6(id)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "enable_ipv6",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def enable_private_networking(id)
body = { :type => "enable_private_networking" }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def enable_private_networking(id)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "enable_private_networking",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,36 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def get_droplet_action(droplet_id, action_id)
request(
:expects => [200],
:method => 'GET',
:path => "v2/droplets/#{droplet_id}/actions/#{action_id}",
)
end
end
class Mock
def get_droplet_action(droplet_id, action)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "change_kernel",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,84 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def get_server_details(server_id)
request(
:expects => [200],
:method => 'GET',
:path => "/v2/droplets/#{server_id}"
)
end
end
# noinspection RubyStringKeysInHashInspection
class Mock
def get_server_details(_)
response = Excon::Response.new
response.status = 200
response.body = {
'droplet' => {
'id' => 3164494,
'name' => 'example.com',
'memory' => 512,
'vcpus' => 1,
'disk' => 20,
'locked' => false,
'status' => 'active',
'kernel' => {
'id' => 2233,
'name' => 'Ubuntu 14.04 x64 vmlinuz-3.13.0-37-generic',
'version' => '3.13.0-37-generic'
},
'created_at' => '2014-11-14T16:36:31Z',
'features' => %w(ipv6 virtio),
'backup_ids' => [],
'snapshot_ids' => [7938206],
'image' => {
'id' => 6918990,
'name' => '14.04 x64',
'distribution' => 'Ubuntu',
'slug' => 'ubuntu-14-04-x64',
'public' => true,
'regions' => %w(nyc1 ams1 sfo1 nyc2 ams2 sgp1 lon1 nyc3 ams3 nyc3),
'created_at' => '2014-10-17T20:24:33Z',
'type' => 'snapshot',
'min_disk_size' => 20
},
'size' => {},
'size_slug' => '512mb',
'networks' => {
'v4' => [
{
'ip_address' => '104.131.186.241',
'netmask' => '255.255.240.0',
'gateway' => '104.131.176.1',
'type' => 'public'
}
],
'v6' => [
{
'ip_address' => '2604:A880:0800:0010:0000:0000:031D:2001',
'netmask' => 64,
'gateway' => '2604:A880:0800:0010:0000:0000:0000:0001',
'type' => 'public'
}
]
},
'region' => {
'name' => 'New York 3',
'slug' => 'nyc3',
'sizes' => %w(32gb 16gb 2gb 1gb 4gb 8gb 512mb 64gb 48gb),
'features' => %w(virtio private_networking backups ipv6 metadata),
'available' => true
}
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,34 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def get_ssh_key(key_id)
request(
:expects => [200],
:method => 'GET',
:path => "/v2/account/keys/#{key_id}"
)
end
end
# noinspection RubyStringKeysInHashInspection
class Mock
def get_ssh_key(_)
response = Excon::Response.new
response.status = 200
response.body = {
'ssh_key' => {
'id' => 512190,
'fingerprint' => '3b:16:bf:e4:8b:00:8b:b8:59:8c:a9:d3:f0:19:45:fa',
'public_key' => 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQDDHr/jh2Jy4yALcK4JyWbVkPRaWmhck3IgCoeOO3z1e2dBowLh64QAM+Qb72pxekALga2oi4GvT+TlWNhzPH4V example',
'name' => 'My SSH Public Key'
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,38 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def list_droplet_actions(id)
request(
:expects => [200],
:method => 'GET',
:path => "v2/droplets/#{id}/actions",
)
end
end
class Mock
def list_droplet_actions(id)
response = Excon::Response.new
response.status = 201
response.body = {
'actions' => [
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "change_kernel",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
]
}
response
end
end
end
end
end

View file

@ -0,0 +1,131 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def list_flavors
request(
:expects => [200],
:method => 'GET',
:path => '/v2/sizes'
)
end
end
end
# noinspection RubyStringKeysInHashInspection
class Mock
def list_flavors
response = Excon::Response.new
response.status = 200
response.body = {
'sizes' => [
{
'slug' => '512mb',
'memory' => 512,
'vcpus' => 1,
'disk' => 20,
'transfer' => 1.0,
'price_monthly' => 5.0,
'price_hourly' => 0.00744,
'regions' => %w(nyc1 sgp1 ams1 ams2 sfo1 nyc2 lon1 nyc3 ams3),
'available' => true
},
{
'slug' => '1gb',
'memory' => 1024,
'vcpus' => 1,
'disk' => 30,
'transfer' => 2.0,
'price_monthly' => 10.0,
'price_hourly' => 0.01488,
'regions' => %w(nyc2 sgp1 ams1 sfo1 lon1 nyc3 ams3 ams2 nyc1),
'available' => true
},
{
'slug' => '2gb',
'memory' => 2048,
'vcpus' => 2,
'disk' => 40,
'transfer' => 3.0,
'price_monthly' => 20.0,
'price_hourly' => 0.02976,
'regions' => %w(nyc2 sfo1 ams1 sgp1 ams2 lon1 nyc3 ams3 nyc1),
'available' => true
},
{
'slug' => '4gb',
'memory' => 4096,
'vcpus' => 2,
'disk' => 60,
'transfer' => 4.0,
'price_monthly' => 40.0,
'price_hourly' => 0.05952,
'regions' => %w(nyc2 sfo1 ams1 sgp1 ams2 lon1 nyc3 ams3 nyc1),
'available' => true
},
{
'slug' => '8gb',
'memory' => 8192,
'vcpus' => 4,
'disk' => 80,
'transfer' => 5.0,
'price_monthly' => 80.0,
'price_hourly' => 0.11905,
'regions' => %w(nyc2 sfo1 sgp1 ams1 ams2 nyc1 lon1 nyc3 ams3),
'available' => true
},
{
'slug' => '16gb',
'memory' => 16384,
'vcpus' => 8,
'disk' => 160,
'transfer' => 6.0,
'price_monthly' => 160.0,
'price_hourly' => 0.2381,
'regions' => %w(sgp1 nyc1 sfo1 ams2 nyc3 lon1 nyc2 ams1 ams3),
'available' => true
},
{
'slug' => '32gb',
'memory' => 32768,
'vcpus' => 12,
'disk' => 320,
'transfer' => 7.0,
'price_monthly' => 320.0,
'price_hourly' => 0.47619,
'regions' => %w(nyc2 sgp1 ams2 nyc1 sfo1 lon1 ams3 nyc3),
'available' => true
},
{
'slug' => '48gb',
'memory' => 49152,
'vcpus' => 16,
'disk' => 480,
'transfer' => 8.0,
'price_monthly' => 480.0,
'price_hourly' => 0.71429,
'regions' => %w(sgp1 ams2 sfo1 nyc1 lon1 nyc2 ams3 nyc3),
'available' => true
},
{
'slug' => '64gb',
'memory' => 65536,
'vcpus' => 20,
'disk' => 640,
'transfer' => 9.0,
'price_monthly' => 640.0,
'price_hourly' => 0.95238,
'regions' => %w(sgp1 nyc1 nyc2 sfo1 lon1 ams3 ams2 nyc3),
'available' => true
}
],
'links' => {},
'meta' => {'total' => 9}
}
response
end
end
end
end

View file

@ -0,0 +1,50 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def list_images
request(
:expects => [200],
:method => 'GET',
:path => '/v2/images'
)
end
end
end
# noinspection RubyStringKeysInHashInspection
class Mock
def list_images
response = Excon::Response.new
response.status = 200
response.body = {
'images' => [
{
'id' => 7555620,
'name' => 'Nifty New Snapshot',
'distribution' => 'Ubuntu',
'slug' => null,
'public' => false,
'regions' => %w(nyc2 nyc3),
'created_at' => '2014-11-04T22:23:02Z',
'type' => 'snapshot',
'min_disk_size' => 20,
}
],
'links' => {
'pages' => {
'last' => 'https://api.digitalocean.com/v2/images?page=56&per_page=1',
'next' => 'https://api.digitalocean.com/v2/images?page=2&per_page=1'
}
},
'meta' => {
'total' => 56
}
}
response
end
end
end
end

View file

@ -0,0 +1,94 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def list_regions
request(
:expects => [200],
:method => 'GET',
:path => '/v2/regions'
)
end
end
end
# noinspection RubyStringKeysInHashInspection
class Mock
def list_images
response = Excon::Response.new
response.status = 200
response.body = {
'regions' => [
{
'name' => 'New York 1',
'slug' => 'nyc1',
'sizes' => [],
'features' => %w(virtio backups),
'available' => false
},
{
'name' => 'Amsterdam 1',
'slug' => 'ams1',
'sizes' => [],
'features' => %w(virtio backups),
'available' => false
},
{
'name' => 'San Francisco 1',
'slug' => 'sfo1',
'sizes' => %w(32gb 16gb 2gb 1gb 4gb 8gb 512mb 64gb 48gb),
'features' => %w(virtio backups metadata),
'available' => true
},
{
'name' => 'New York 2',
'slug' => 'nyc2',
'sizes' => %w(32gb 16gb 2gb 1gb 4gb 8gb 512mb 64gb 48gb),
'features' => %w(virtio private_networking backups),
'available' => true
},
{
'name' => 'Amsterdam 2',
'slug' => 'ams2',
'sizes' => %w(32gb 16gb 2gb 1gb 4gb 8gb 512mb 64gb 48gb),
'features' => %w(virtio private_networking backups metadata),
'available' => true
},
{
'name' => 'Singapore 1',
'slug' => 'sgp1',
'sizes' => %w(32gb 16gb 2gb 1gb 4gb 8gb 512mb 64gb 48gb),
'features' => %w(virtio private_networking backups ipv6 metadata),
'available' => true
},
{
'name' => 'London 1',
'slug' => 'lon1',
'sizes' => %w(32gb 16gb 2gb 1gb 4gb 8gb 512mb 64gb 48gb),
'features' => %w(virtio private_networking backups ipv6 metadata),
'available' => true
},
{
'name' => 'New York 3',
'slug' => 'nyc3',
'sizes' => %w(32gb 16gb 2gb 1gb 4gb 8gb 512mb 64gb 48gb),
'features' => %w(virtio private_networking backups ipv6 metadata),
'available' => true
},
{
'name' => 'Amsterdam 3',
'slug' => 'ams3',
'sizes' => %w(32gb 16gb 2gb 1gb 4gb 8gb 512mb 64gb 48gb),
'features' => %w(virtio private_networking backups ipv6 metadata),
'available' => true
}
],
'links' => {},
'meta' => {'total' => 9}
}
response
end
end
end
end

View file

@ -0,0 +1,28 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def list_servers
request(
:expects => [200],
:method => 'GET',
:path => '/v2/droplets'
)
end
end
# noinspection RubyStringKeysInHashInspection
class Mock
def list_servers
response = Excon::Response.new
response.status = 200
response.body = {
"status" => "OK",
"droplets" => self.data[:servers]
}
response
end
end
end
end
end

View file

@ -0,0 +1,26 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def list_ssh_keys
request(
:expects => [200],
:method => 'GET',
:path => 'v2/account/keys',
)
end
end
# noinspection RubyStringKeysInHashInspection
class Mock
def list_ssh_keys
response = Excon::Response.new
response.status = 200
response.body = {
}
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def password_reset(id)
body = { :type => "password_reset" }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def password_reset(id)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "password_reset",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def power_cycle(id)
body = { :type => "power_cycle" }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def power_cycle(id)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "power_cycle",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def power_off(id)
body = { :type => "power_off" }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def power_off(id)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "power_off",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def power_on(id)
body = { :type => "power_on" }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def power_on(id)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "power_on",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def reboot_server(id)
body = { :type => "reboot" }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def reboot_server(id)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "reboot",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def rebuild(id, image)
body = { :type => "rebuild", :image => image }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def rebuild(id, image)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "rebuild",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def rename(id, name)
body = { :type => "rename", :name => name }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def rename(id, name)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "rename",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,48 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def resize(id, resize_disk, size)
body = {
:type => "resize",
:disk => resize_disk,
:size => size
}
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def resize(id, resize_disk, size)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "resize",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def restore(id, image)
body = { :type => "restore", :image => image }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def restore(id, image)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "restore",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def shutdown(id)
body = { :type => "shutdown" }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def shutdown(id)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "shutdown",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def snapshot(id, name)
body = { :type => "snapshot", :name => name }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def snapshot(id, name)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "snapshot",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,46 @@
module Fog
module Compute
class DigitalOceanV2
# noinspection RubyStringKeysInHashInspection
class Real
def update_ssh_key(key_id, name)
update_options = {
:name => name,
}
encoded_body = Fog::JSON.encode(update_options)
request(
:expects => [200],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'PUT',
:path => "/v2/account/keys/#{key_id}",
:body => encoded_body,
)
end
end
# noinspection RubyStringKeysInHashInspection
class Mock
def update_server(key_id, name)
response = Excon::Response.new
response.status = 200
response.body ={
'ssh_key' => {
'id' => 512190,
'fingerprint' => "3b:16:bf:e4:8b:00:8b:b8:59:8c:a9:d3:f0:19:45:fa",
'public_key' => "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQDDHr/jh2Jy4yALcK4JyWbVkPRaWmhck3IgCoeOO3z1e2dBowLh64QAM+Qb72pxekALga2oi4GvT+TlWNhzPH4V example",
'name' => "My SSH Public Key"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module Compute
class DigitalOceanV2
class Real
def upgrade(id)
body = { :type => "upgrade" }
encoded_body = Fog::JSON.encode(body)
request(
:expects => [201],
:headers => {
'Content-Type' => "application/json; charset=UTF-8",
},
:method => 'POST',
:path => "v2/droplets/#{id}/actions",
:body => encoded_body,
)
end
end
class Mock
def upgrade(id)
response = Excon::Response.new
response.status = 201
response.body = {
'action' => {
'id' => Fog::Mock.random_numbers(1).to_i,
'status' => "in-progress",
'type' => "upgrade",
'started_at' => "2014-11-14T16:31:00Z",
'completed_at' => null,
'resource_id' => id,
'resource_type' => "droplet",
'region' => "nyc3",
'region_slug' => "nyc3"
}
}
response
end
end
end
end
end

View file

@ -0,0 +1,51 @@
module Fog
module DigitalOcean
class Service
def request(params, parse_json = true)
first_attempt = true
begin
response = @connection.request(request_params(params))
rescue Excon::Errors::Unauthorized => error
raise error unless first_attempt
first_attempt = false
authenticate
retry
end
process_response(response) if parse_json
response
end
private
def process_response(response)
if response &&
response.body &&
response.body.is_a?(String) &&
!response.body.strip.empty?
begin
response.body = Fog::JSON.decode(response.body)
rescue Fog::JSON::DecodeError => e
Fog::Logger.warning("Error Parsing response json - #{e}")
response.body = {}
end
end
end
def headers(options={})
{'Content-Type' => 'application/json',
'Accept' => 'application/json',
}.merge(options[:headers] || {})
end
def request_params(params)
params.merge({
:headers => headers(params),
:path => "#{params[:path]}"
})
end
end
end
end

View file

@ -0,0 +1,25 @@
Shindo.tests('Fog::Compute::DigitalOceanV2 | list_flavors request', ['digitalocean', 'compute']) do
service = Fog::Compute.new(:provider => 'DigitalOcean', :version => 'V2')
size_format = {
'slug' => String,
'memory' => Integer,
'vcpus' => Integer,
'disk' => Integer,
'transfer' => Float,
'price_monthly' => Float,
'price_hourly' => Float,
'regions' => Array,
'available' => Fog::Boolean,
}
tests('success') do
tests('#list_flavors') do
service.list_flavors.body['sizes'].each do |size|
tests('format').data_matches_schema(size_format) do
size
end
end
end
end
end

View file

@ -0,0 +1,25 @@
Shindo.tests('Fog::Compute::DigitalOceanV2 | list_images request', ['digitalocean', 'compute']) do
service = Fog::Compute.new(:provider => 'DigitalOcean', :version => 'V2')
image_format = {
'id' => Integer,
'name' => String,
'type' => String,
'distribution' => String,
'slug' => Fog::Nullable::String,
'public' => Fog::Boolean,
'regions' => Array,
'min_disk_size' => Fog::Nullable::Integer,
'created_at' => String,
}
tests('success') do
tests('#list_images') do
service.list_images.body['images'].each do |image|
tests('format').data_matches_schema(image_format) do
image
end
end
end
end
end

View file

@ -0,0 +1,21 @@
Shindo.tests('Fog::Compute::DigitalOceanV2 | list_regions request', ['digitalocean', 'compute']) do
service = Fog::Compute.new(:provider => 'DigitalOcean', :version => 'V2')
region_format = {
'slug' => String,
'name' => String,
'sizes' => Array,
'available' => Fog::Boolean,
'features' => Array,
}
tests('success') do
tests('#list_regions') do
service.list_regions.body['regions'].each do |region|
tests('format').data_matches_schema(region_format) do
region
end
end
end
end
end

View file

@ -0,0 +1,75 @@
Shindo.tests('Fog::Compute::DigitalOceanV2 | create_server request', ['digitalocean', 'compute']) do
service = Fog::Compute.new(:provider => 'DigitalOcean', :version => 'V2')
server_format = {
'id' => Integer,
'name' => String,
'memory' => Integer,
'vcpus' => Integer,
'disk' => Integer,
'locked' => Fog::Boolean,
'created_at' => String,
}
create_server_format = {
'droplet' => {
'id' => Integer,
'name' => String,
'memory' => Integer,
'vcpus' => Integer,
'disk' => Integer,
'locked' => Fog::Boolean,
'status' => String,
'kernel' => Hash,
"created_at" => String,
"features" => Array,
"backup_ids" => Array,
"snapshot_ids" => Array,
"image" => Hash,
"size" => Hash,
"size_slug" => String,
"networks" => Hash,
"region" => Hash,
},
'links' => {
"actions" => Array,
}
}
tests('success') do
server_id = nil
server_name = "fog-#{Time.now.to_i.to_s}"
image = "ubuntu-14-04-x64"
region = "nyc3"
size = "512mb"
tests("#create_server(#{server_name}, #{size}, #{image}, #{region})").formats(create_server_format) do
body = service.create_server(server_name, size, image, region).body
server_id = body['droplet']['id']
body
end
test('#get_server_details can retrieve by id') do
body = service.get_server_details(server_id).body
body['droplet']['name'] == server_name
end
server = service.servers.get(server_id)
server.wait_for { ready? }
tests('#delete_server').succeeds do
server.delete
end
tests('#list_servers') do
service.list_servers.body['droplets'].each do |droplet|
tests('format').data_matches_schema(server_format) do
droplet
end
end
end
end
end