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

Merge branch 'master' of github.com:fog/fog into json_lib

This commit is contained in:
Kyle Rames 2013-04-03 08:43:06 -05:00
commit 1fd1303977
57 changed files with 1835 additions and 7 deletions

View file

@ -7,7 +7,7 @@ Gem::Specification.new do |s|
## the sub! line in the Rakefile
s.name = 'fog'
s.version = '1.10.0'
s.date = '2013-03-05'
s.date = '2013-03-30'
s.rubyforge_project = 'fog'
## Make sure your summary is short. The description may be as long

View file

@ -1,3 +1,8 @@
# necessary when requiring fog without rubygems while also
# maintaining ruby 1.8.7 support (can't use require_relative)
__LIB_DIR__ = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift __LIB_DIR__ unless $LOAD_PATH.include?(__LIB_DIR__)
# any one of these can be required separately.
# they all depend on fog/core for shared functionality.
require 'fog/atmos'
@ -6,6 +11,7 @@ require 'fog/bluebox'
require 'fog/brightbox'
require 'fog/cloudstack'
require 'fog/clodo'
require 'fog/digitalocean'
require 'fog/dnsimple'
require 'fog/dnsmadeeasy'
require 'fog/dreamhost'

View file

@ -43,7 +43,7 @@ module Fog
if INVALID_LOCATIONS.include?(new_location)
raise ArgumentError, "location must not include any of #{INVALID_LOCATIONS.join(', ')}. See http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUT.html"
else
merge_attributes(:location => new_location)
@location = new_location
end
end

View file

@ -63,6 +63,7 @@ require 'fog/bin/bluebox'
require 'fog/bin/brightbox'
require 'fog/bin/cloudstack'
require 'fog/bin/clodo'
require 'fog/bin/digitalocean'
require 'fog/bin/dnsimple'
require 'fog/bin/dnsmadeeasy'
require 'fog/bin/dreamhost'

View file

@ -0,0 +1,31 @@
class DigitalOcean < Fog::Bin
class << self
def class_for(key)
case key
when :compute
Fog::Compute::DigitalOcean
else
raise ArgumentError, "Unsupported #{self} service: #{key}"
end
end
def [](service)
@@connections ||= Hash.new do |hash, key|
hash[key] = case key
when :compute
Fog::Logger.warning("DigitalOcean[:compute] is not recommended, use Compute[:digitalocean] for portability")
Fog::Compute.new(:provider => 'DigitalOcean')
else
raise ArgumentError, "Unrecognized service: #{key.inspect}"
end
end
@@connections[service]
end
def services
Fog::DigitalOcean.services
end
end
end

View file

@ -1,8 +1,3 @@
__LIB_DIR__ = File.expand_path(File.join(File.dirname(__FILE__), '..'))
$LOAD_PATH.unshift __LIB_DIR__ unless
$LOAD_PATH.include?(__LIB_DIR__)
# external core dependencies
require 'base64'
require 'cgi'

9
lib/fog/digitalocean.rb Normal file
View file

@ -0,0 +1,9 @@
require 'fog/core'
module Fog
module DigitalOcean
extend Fog::Provider
service(:compute, 'digitalocean/compute', 'Compute')
end
end

View file

@ -0,0 +1,107 @@
require 'fog/digitalocean'
require 'fog/compute'
module Fog
module Compute
class DigitalOcean < Fog::Service
requires :digitalocean_api_key
requires :digitalocean_client_id
recognizes :digitalocean_api_url
model_path 'fog/digitalocean/models/compute'
model :server
collection :servers
model :flavor
collection :flavors
model :image
collection :images
model :region
collection :regions
model :ssh_key
collection :ssh_keys
request_path 'fog/digitalocean/requests/compute'
request :list_servers
request :list_images
request :list_regions
request :list_flavors
request :get_server_details
request :create_server
request :destroy_server
request :reboot_server
request :power_cycle_server
request :power_off_server
request :power_on_server
request :shutdown_server
request :list_ssh_keys
request :create_ssh_key
request :get_ssh_key
request :destroy_ssh_key
# request :digitalocean_resize
class Mock
def self.data
@data ||= Hash.new do |hash, key|
hash[key] = {
:servers => [],
:ssh_keys => []
}
end
end
def self.reset
@data = nil
end
def initialize(options={})
@digitalocean_api_key = options[:digitalocean_api_key]
end
def data
self.class.data[@digitalocean_api_key]
end
def reset_data
self.class.data.delete(@digitalocean_api_key)
end
end
class Real
def initialize(options={})
@digitalocean_api_key = options[:digitalocean_api_key]
@digitalocean_client_id = options[:digitalocean_client_id]
@digitalocean_api_url = options[:digitalocean_api_url] || \
"https://api.digitalocean.com"
@connection = Fog::Connection.new(@digitalocean_api_url)
end
def reload
@connection.reset
end
def request(params)
params[:query] ||= {}
params[:query].merge!(:api_key => @digitalocean_api_key)
params[:query].merge!(:client_id => @digitalocean_client_id)
response = @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
end
end
response
end
end
end
end
end

View file

@ -0,0 +1,106 @@
# Getting started: the compute service
You'll need a DigitalOcean account and API key to use this provider.
Get one from http://www.digitalocean.com.
To generate the API key, login to the DigitalOcean web panel and go to
'My Settings -> API Access -> Generate a new API key'.
Write down the Client Key and API Key, you'll need both to use the service.
## Connecting, retrieving and managing server objects
Before we start, I guess it will be useful to the reader to know
that Fog servers are 'droplets' in DigitalOcean's parlance.
'Server' is the Fog way to name VMs, and we have
respected that in the DigitalOcean's Fog provider.
First, create a connection to the host:
```ruby
require 'fog'
do = Fog::Compute.new({
:provider => 'DigitalOcean',
:digitalocean_api_key => 'poiuweoruwoeiuroiwuer', # your API key here
:digitalocean_client_id => 'lkjasoidfuoiu' # your client key here
})
```
## Listing servers
Listing servers and attributes:
```ruby
docean.servers.each do |server|
# remember, servers are droplets
server.id
server.name
server.state
server.backups_enabled
server.image_id
server.flavor_id # server 'size' in DigitalOcean's API parlance
server.region_id
end
```
## Server creation and life-cycle management
Creating a new server (droplet):
```ruby
server = docean.servers.create :name => 'foobar',
# use the first image listed
:image_id => docean.images.first.id,
# use the first flavor listed
:flavor_id => docean.flavors.first.id,
# use the first region listed
:region_id => docean.regions.first.id
```
The server is automatically started after that.
We didn't pay attention when choosing the flavor, image and region used
but you can easily list them too, and then decide:
```ruby
docean.images.each do |image|
image.id
image.name
image.distribution
end
docean.flavors.each do |flavor|
flavor.id
flavor.name
end
docean.regions.each do |region|
region.id
region.name
end
```
Rebooting a server:
```ruby
server = docean.servers.first
server.reboot
```
Power cycle a server:
```ruby
server.power_cycle
```
Destroying the server:
```ruby
server.destroy
```

View file

@ -0,0 +1,14 @@
require 'fog/core/model'
module Fog
module Compute
class DigitalOcean
class Flavor < Fog::Model
identity :id
attribute :name
end
end
end
end

View file

@ -0,0 +1,25 @@
require 'fog/core/collection'
require 'fog/digitalocean/models/compute/flavor'
module Fog
module Compute
class DigitalOcean
class Flavors < Fog::Collection
model Fog::Compute::DigitalOcean::Flavor
def all
load service.list_flavors.body['sizes']
end
def get(id)
all.find { |f| f.id == id }
rescue Fog::Errors::NotFound
nil
end
end
end
end
end

View file

@ -0,0 +1,15 @@
require 'fog/core/model'
module Fog
module Compute
class DigitalOcean
class Image < Fog::Model
identity :id
attribute :name
attribute :distribution
end
end
end
end

View file

@ -0,0 +1,25 @@
require 'fog/core/collection'
require 'fog/digitalocean/models/compute/image'
module Fog
module Compute
class DigitalOcean
class Images < Fog::Collection
model Fog::Compute::DigitalOcean::Image
def all
load service.list_images.body['images']
end
def get(id)
all.find { |f| f.id == id }
rescue Fog::Errors::NotFound
nil
end
end
end
end
end

View file

@ -0,0 +1,14 @@
require 'fog/core/model'
module Fog
module Compute
class DigitalOcean
class Region < Fog::Model
identity :id
attribute :name
end
end
end
end

View file

@ -0,0 +1,25 @@
require 'fog/core/collection'
require 'fog/digitalocean/models/compute/region'
module Fog
module Compute
class DigitalOcean
class Regions < Fog::Collection
model Fog::Compute::DigitalOcean::Region
def all
load service.list_regions.body['regions']
end
def get(id)
all.find { |f| f.id == id }
rescue Fog::Errors::NotFound
nil
end
end
end
end
end

View file

@ -0,0 +1,150 @@
require 'fog/compute/models/server'
module Fog
module Compute
class DigitalOcean
# A DigitalOcean Droplet
#
class Server < Fog::Compute::Server
identity :id
attribute :name
attribute :state, :aliases => 'status'
attribute :image_id
attribute :region_id
attribute :flavor_id, :aliases => 'size_id'
# Not documented in their API, but
# available nevertheless
attribute :ip_address
attribute :backups_active
# Reboot the server (soft reboot).
#
# The preferred method of rebooting a server.
def reboot
requires :id
service.reboot_server self.id
end
# Reboot the server (hard reboot).
#
# Powers the server off and then powers it on again.
def power_cycle
requires :id
service.power_cycle_server self.id
end
# Shutdown the server
#
# Sends a shutdown signal to the operating system.
# 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 shutdown
requires :id
service.shutdown_server self.id
end
# Power off the server
#
# 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
service.power_off_server self.id
end
# Power on the server.
#
# 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,
# it is charged for an hour.
#
def start
requires :id
service.power_on_server self.id
end
# Creates the server (not to be called directly).
#
# Usually called by Fog::Collection#create
#
# do = Fog::Compute.new({
# :provider => 'DigitalOcean',
# :digitalocean_api_key => 'key-here', # your API key here
# :digitalocean_client_id => 'client-id-here' # your client key here
# })
# do.servers.create :name => 'foobar',
# :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?
requires :name, :flavor_id, :image_id, :region_id
options = {}
if attributes[:ssh_key_ids]
options[:ssh_key_ids] = attributes[:ssh_key_ids]
end
data = service.create_server name,
flavor_id,
image_id,
region_id,
options
merge_attributes(data.body['droplet'])
true
end
# Destroy the server, freeing up the resources.
#
# DigitalOcean will stop charging you for the resources
# the server was using.
#
# Once the server has been destroyed, there's no way
# to recover it so the data is irrecoverably lost.
#
# 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
# DigitalOcean will keep charging you.
# Double checked this with DigitalOcean staff and confirmed
# that it's the way it works right now.
#
# Double check the server has been destroyed!
def destroy
requires :id
service.destroy_server id
end
# Checks whether the server status is 'active'.
#
# The server transitions from 'new' to 'active' sixty to ninety
# seconds after creating it (time varies and may take more
# than 90 secs).
#
# @return [Boolean]
def ready?
state == 'active'
end
# DigitalOcean API does not support updating server state
def update
msg = 'DigitalOcean servers do not support updates'
raise NotImplementedError.new(msg)
end
end
end
end
end

View file

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

View file

@ -0,0 +1,28 @@
module Fog
module Compute
class DigitalOcean
class SshKey < Fog::Model
identity :id
attribute :name
attribute :ssh_pub_key
def save
requires :name, :ssh_pub_key
merge_attributes(service.create_ssh_key(name, ssh_pub_key).body['ssh_key'])
true
end
def destroy
requires :id
service.destroy_ssh_key id
true
end
end
end
end
end

View file

@ -0,0 +1,27 @@
require 'fog/digitalocean/models/compute/ssh_key'
module Fog
module Compute
class DigitalOcean
class SshKeys < Fog::Collection
identity :href
model Fog::Compute::DigitalOcean::SshKey
def all
data = service.list_ssh_keys.body['ssh_keys']
load(data)
end
def get(uri)
if data = service.get_ssh_key(uri)
new(data.body)
end
rescue Fog::Errors::NotFound
nil
end
end
end
end
end

View file

@ -0,0 +1,68 @@
module Fog
module Compute
class DigitalOcean
class Real
#
# FIXME: missing ssh keys support
#
def create_server( name,
size_id,
image_id,
region_id,
options = {} )
query_hash = {
:name => name,
:size_id => size_id,
:image_id => image_id,
:region_id => region_id
}
if options[:ssh_key_ids]
query_hash[:ssh_key_ids] = options[:ssh_key_ids]
end
request(
:expects => [200],
:method => 'GET',
:path => 'droplets/new',
:query => query_hash
)
end
end
class Mock
def create_server( name,
size_id,
image_id,
region_id,
options = {} )
response = Excon::Response.new
response.status = 200
mock_data = {
"id" => Fog::Mock.random_numbers(1).to_i,
"event_id" => Fog::Mock.random_numbers(2).to_i,
"name" => name,
"size_id" => size_id,
"image_id" => image_id,
"region_id" => region_id,
"status" => 'active'
}
response.body = {
"status" => "OK",
"droplet" => mock_data
}
self.data[:servers] << mock_data
response
end
end
end
end
end

View file

@ -0,0 +1,38 @@
module Fog
module Compute
class DigitalOcean
class Real
def create_ssh_key( name, pub_key )
request(
:expects => [200],
:method => 'GET',
:path => 'ssh_keys/new',
:query => { 'name' => name, 'ssh_pub_key' => pub_key }
)
end
end
class Mock
def create_ssh_key( name, pub_key )
response = Excon::Response.new
response.status = 200
mock_data = {
"id" => Fog::Mock.random_numbers(1).to_i,
"name" => name,
"ssh_pub_key" => pub_key
}
response.body = {
"status" => "OK",
"ssh_key" => mock_data
}
self.data[:ssh_keys] << mock_data
response
end
end
end
end
end

View file

@ -0,0 +1,34 @@
module Fog
module Compute
class DigitalOcean
class Real
#
# FIXME: missing ssh keys support
#
def destroy_server( id )
request(
:expects => [200],
:method => 'GET',
:path => "droplets/#{id}/destroy"
)
end
end
class Mock
def destroy_server( id )
response = Excon::Response.new
response.status = 200
response.body = {
"event_id" => Fog::Mock.random_numbers(1).to_i,
"status" => "OK"
}
response
end
end
end
end
end

View file

@ -0,0 +1,37 @@
module Fog
module Compute
class DigitalOcean
class Real
#
# Delete a SSH public key from your account
#
# @see https://www.digitalocean.com/api#ssh_keys
#
def destroy_ssh_key(id)
request(
:expects => [200],
:method => 'GET',
:path => "ssh_keys/#{id}/destroy"
)
end
end
class Mock
def destroy_ssh_key(id)
response = Excon::Response.new
response.status = 200
if self.data[:ssh_keys].reject! { |k| k['id'] == id }
response.body = { "status" => "OK" }
else
response.body = { "status" => "ERROR" }
end
response
end
end
end
end
end

View file

@ -0,0 +1,35 @@
module Fog
module Compute
class DigitalOcean
class Real
def get_server_details(server_id)
request(
:expects => [200],
:method => 'GET',
:path => "droplets/#{server_id}"
)
end
end
class Mock
def get_server_details(server_id)
response = Excon::Response.new
response.status = 200
server = self.data[:servers].find { |s| s['id'] == server_id }
response.body = {
"status" => "OK",
"droplet" => self.data[:servers].find { |s| s['id'] == server_id }
}
response
end
end
end
end
end

View file

@ -0,0 +1,39 @@
module Fog
module Compute
class DigitalOcean
class Real
#
# This method shows a specific public SSH key in your account
# that can be added to a droplet.
#
# @see https://www.digitalocean.com/api#ssh_keys
#
def get_ssh_key(id)
request(
:expects => [200],
:method => 'GET',
:path => "ssh_keys/#{id}"
)
end
end
class Mock
def get_ssh_key(id)
response = Excon::Response.new
response.status = 200
response.body = {
"status" => "OK",
# key listing does not return ssh_pub_key
# https://www.digitalocean.com/api#ssh_keys
"ssh_key" => self.data[:ssh_keys].find { |k| k['id'] == id }
}
response
end
end
end
end
end

View file

@ -0,0 +1,38 @@
module Fog
module Compute
class DigitalOcean
class Real
def list_flavors(options = {})
request(
:expects => [200],
:method => 'GET',
:path => 'sizes'
)
end
end
class Mock
def list_flavors
response = Excon::Response.new
response.status = 200
response.body = {
"status" => "OK",
"sizes" => [
{"id" => 33,"name" => "512MB"},
{"id" => 34,"name" => "1GB"},
{"id" => 35,"name" => "2GB"},
{"id" => 36,"name" => "4GB"},
{"id" => 37,"name" => "8GB"},
{"id" => 38,"name" => "16GB"}
]
}
response
end
end
end
end
end

View file

@ -0,0 +1,49 @@
module Fog
module Compute
class DigitalOcean
class Real
def list_images(options = {})
request(
:expects => [200],
:method => 'GET',
:path => 'images'
)
end
end
class Mock
def list_images
response = Excon::Response.new
response.status = 200
response.body = {
"status" => "OK",
"images" => [
# Sample image
{
"id" => 1601,
"name" => "CentOS 5.8 x64",
"distribution" => "CentOS"
},
{
"id" => 1602,
"name" => "CentOS 5.8 x32",
"distribution" => "CentOS"
},
{
"id" => 2676,
"name" => "Ubuntu 12.04 x64 Server",
"distribution" => "Ubuntu"
},
]
}
response
end
end
end
end
end

View file

@ -0,0 +1,34 @@
module Fog
module Compute
class DigitalOcean
class Real
def list_regions(options = {})
request(
:expects => [200],
:method => 'GET',
:path => 'regions'
)
end
end
class Mock
def list_regions
response = Excon::Response.new
response.status = 200
response.body = {
"status" => "OK",
"regions" => [
{"id" => 1,"name" => "New York 1"},
{"id" => 2,"name" => "Amsterdam 1"}
]
}
response
end
end
end
end
end

View file

@ -0,0 +1,31 @@
module Fog
module Compute
class DigitalOcean
class Real
def list_servers(options = {})
request(
:expects => [200],
:method => 'GET',
:path => 'droplets'
)
end
end
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,31 @@
module Fog
module Compute
class DigitalOcean
class Real
def list_ssh_keys(options = {})
request(
:expects => [200],
:method => 'GET',
:path => 'ssh_keys'
)
end
end
class Mock
def list_ssh_keys
response = Excon::Response.new
response.status = 200
response.body = {
"status" => "OK",
"ssh_keys" => self.data[:ssh_keys]
}
response
end
end
end
end
end

View file

@ -0,0 +1,33 @@
module Fog
module Compute
class DigitalOcean
class Real
def power_cycle_server( id )
request(
:expects => [200],
:method => 'GET',
:path => "droplets/#{id}/power_cycle"
)
end
end
class Mock
def power_cycle_server( id )
response = Excon::Response.new
response.status = 200
server = self.data[:servers].find { |s| s['id'] == id }
server['status'] = 'off' if server
response.body = {
"event_id" => Fog::Mock.random_numbers(1).to_i,
"status" => "OK"
}
response
end
end
end
end
end

View file

@ -0,0 +1,33 @@
module Fog
module Compute
class DigitalOcean
class Real
def power_off_server( id )
request(
:expects => [200],
:method => 'GET',
:path => "droplets/#{id}/power_off"
)
end
end
class Mock
def power_off_server( id )
response = Excon::Response.new
response.status = 200
server = self.data[:servers].find { |s| s['id'] }
server['status'] = 'off' if server
response.body = {
"event_id" => Fog::Mock.random_numbers(1).to_i,
"status" => "OK"
}
response
end
end
end
end
end

View file

@ -0,0 +1,33 @@
module Fog
module Compute
class DigitalOcean
class Real
def power_on_server( id )
request(
:expects => [200],
:method => 'GET',
:path => "droplets/#{id}/power_on"
)
end
end
class Mock
def power_on_server( id )
response = Excon::Response.new
response.status = 200
server = self.data[:servers].find { |s| s['id'] }
server['status'] = 'active' if server
response.body = {
"event_id" => Fog::Mock.random_numbers(1).to_i,
"status" => "OK"
}
response
end
end
end
end
end

View file

@ -0,0 +1,33 @@
module Fog
module Compute
class DigitalOcean
class Real
def reboot_server( id )
request(
:expects => [200],
:method => 'GET',
:path => "droplets/#{id}/reboot"
)
end
end
class Mock
def reboot_server( id )
response = Excon::Response.new
response.status = 200
server = self.data[:servers].find { |s| s['id'] == id }
server['status'] = 'off' if server
response.body = {
"event_id" => Fog::Mock.random_numbers(1).to_i,
"status" => "OK"
}
response
end
end
end
end
end

View file

@ -0,0 +1,36 @@
module Fog
module Compute
class DigitalOcean
class Real
def shutdown_server( id )
request(
:expects => [200],
:method => 'GET',
:path => "droplets/#{id}/shutdown"
)
end
end
class Mock
def shutdown_server( id )
response = Excon::Response.new
response.status = 200
server = self.data[:servers].find { |s| s['id'] == id }
# Simulate reboot
server['status'] = 'off' if server
response.body = {
"event_id" => Fog::Mock.random_numbers(1).to_i,
"status" => "OK"
}
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
# Shortcut for Fog::Compute[:digitalocean]
def service
Fog::Compute[:digitalocean]
end
# Create a long lived server for the tests
def fog_test_server
server = service.servers.find { |s| s.name == 'fog-test-server' }
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
end
server
end
# Destroy the long lived server
def fog_test_server_destroy
server = service.servers.find { |s| s.name == 'fog-test-server' }
server.destroy if server
end
at_exit do
unless Fog.mocking?
server = service.servers.find { |s| s.name == 'fog-test-server' }
if server
server.wait_for(120) do
reload rescue nil; ready?
end
end
fog_test_server_destroy
end
end

View file

@ -0,0 +1,30 @@
Shindo.tests("Fog::Compute[:digitalocean] | flavor model", ['digitalocean', 'compute']) do
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 = [
:id,
:name,
]
tests("The flavor model should respond to") do
attributes.each do |attribute|
test("#{attribute}") { flavor.respond_to? attribute }
end
end
tests("The attributes hash should have key") do
attributes.each do |attribute|
test("#{attribute}") { model_attribute_hash.has_key? attribute }
end
end
end
end
end

View file

@ -0,0 +1,31 @@
Shindo.tests("Fog::Compute[:digitalocean] | image model", ['digitalocean', 'compute']) do
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 = [
:id,
:name,
:distribution
]
tests("The image model should respond to") do
attributes.each do |attribute|
test("#{attribute}") { image.respond_to? attribute }
end
end
tests("The attributes hash should have key") do
attributes.each do |attribute|
test("#{attribute}") { model_attribute_hash.has_key? attribute }
end
end
end
end
end

View file

@ -0,0 +1,30 @@
Shindo.tests("Fog::Compute[:digitalocean] | region model", ['digitalocean', 'compute']) do
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 = [
:id,
:name,
]
tests("The region model should respond to") do
attributes.each do |attribute|
test("#{attribute}") { region.respond_to? attribute }
end
end
tests("The attributes hash should have key") do
attributes.each do |attribute|
test("#{attribute}") { model_attribute_hash.has_key? attribute }
end
end
end
end
end

View file

@ -0,0 +1,84 @@
Shindo.tests("Fog::Compute[:digitalocean] | server model", ['digitalocean', 'compute']) do
server = fog_test_server
tests('The server model should') do
tests('have the action') do
test('reload') { server.respond_to? 'reload' }
%w{
shutdown
reboot
power_cycle
stop
start
}.each do |action|
test(action) { server.respond_to? action }
end
end
tests('have attributes') do
model_attribute_hash = server.attributes
attributes = [
:id,
:name,
:state,
:backups_active,
:ip_address,
:flavor_id,
:region_id,
:image_id
]
tests("The server model should respond to") do
attributes.each do |attribute|
test("#{attribute}") { server.respond_to? attribute }
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? }
server.power_cycle
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
pending unless Fog.mocking?
server.start
server.wait_for { server.ready? }
server.shutdown
server.wait_for { server.state == 'off' }
server.state == 'off'
end
test('#update') do
begin
server.update
rescue NotImplementedError => e
true
end
end
end
# restore server state
server.start
end

View file

@ -0,0 +1,35 @@
Shindo.tests('Fog::Compute[:digitalocean] | servers collection', ['digitalocean']) do
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
end
end

View file

@ -0,0 +1,40 @@
Shindo.tests("Fog::Compute[:digitalocean] | ssh_key model", ['digitalocean', 'compute']) do
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'
@key.is_a? Fog::Compute::DigitalOcean::SshKey
end
tests('have the action') do
test('reload') { @key.respond_to? 'reload' }
%w{
save
destroy
}.each do |action|
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
end
end

View file

@ -0,0 +1,28 @@
Shindo.tests('Fog::Compute[:digitalocean] | ssh_keys collection', ['digitalocean']) do
service = Fog::Compute[:digitalocean]
tests('The ssh_keys collection') do
key = service.ssh_keys.create :name => 'fookey',
:ssh_pub_key => 'fookey'
[:all, :get].each do |method|
test("should respond to #{method}") do
service.ssh_keys.respond_to? method
end
end
tests('should have Fog::Compute::DigitalOcean::SshKey inside') do
service.ssh_keys.each do |s|
test { s.kind_of? Fog::Compute::DigitalOcean::SshKey }
end
end
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
end
end
end
end

View file

@ -0,0 +1,28 @@
Shindo.tests('Fog::Compute[:digitalocean] | create_server request', ['digitalocean', 'compute']) do
@server_format = {
'id' => Integer,
'name' => String,
'image_id' => Integer,
'size_id' => Integer,
'event_id' => Integer
}
service = Fog::Compute[:digitalocean]
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' }
flavor = service.flavors.find { |f| f.name == '512MB' }
data = Fog::Compute[:digitalocean].create_server 'fog-test-server',
flavor.id,
image.id,
service.regions.first.id
data.body
end
end
end

View file

@ -0,0 +1,22 @@
Shindo.tests('Fog::Compute[:digitalocean] | create_ssh_key request', ['digitalocean', 'compute']) do
@key_format = {
'id' => Integer,
'name' => String,
'ssh_pub_key' => String
}
service = Fog::Compute[:digitalocean]
tests('success') do
tests('#create_ssh_key').formats({'status' => 'OK', 'ssh_key' => @key_format}) do
@key = Fog::Compute[:digitalocean].create_ssh_key 'fookey', 'fookey'
@key.body
end
end
service.destroy_ssh_key @key.body['ssh_key']['id']
end

View file

@ -0,0 +1,15 @@
Shindo.tests('Fog::Compute[:digitalocean] | destroy_server request', ['digitalocean', 'compute']) do
service = Fog::Compute[:digitalocean]
server = fog_test_server
tests('success') do
test('#destroy_server') do
service.destroy_server(server.id).body['status'] == 'OK'
end
end
end

View file

@ -0,0 +1,23 @@
Shindo.tests('Fog::Compute[:digitalocean] | destroy_ssh_key request', ['digitalocean', 'compute']) do
service = Fog::Compute[:digitalocean]
tests('success') do
test('#destroy_ssh_key') do
key = service.create_ssh_key 'fookey', 'fookey'
service.destroy_ssh_key(key.body['ssh_key']['id']).body['status'] == 'OK'
end
end
tests('failures') do
test 'delete invalid key' do
# DigitalOcean API returns 500 with this sometimes
# so mark it as pending in real mode
pending unless Fog.mocking?
service.destroy_ssh_key('00000000000').body['status'] == 'ERROR'
end
end
end

View file

@ -0,0 +1,13 @@
Shindo.tests('Fog::Compute[:digitalocean] | get_server_details request', ['digitalocean', 'compute']) do
tests('success') do
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'
end
end
end

View file

@ -0,0 +1,22 @@
Shindo.tests('Fog::Compute[:digitalocean] | get_ssh_keys request', ['digitalocean', 'compute']) do
@ssh_key_format = {
'id' => Integer,
'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
end
end
end

View file

@ -0,0 +1,23 @@
Shindo.tests('Fog::Compute[:digitalocean] | list_flavors request', ['digitalocean', 'compute']) do
# {"id":2,"name":"Amsterdam 1"}
@flavor_format = {
'id' => Integer,
'name' => String,
}
tests('success') do
tests('#list_flavor') do
flavors = Fog::Compute[:digitalocean].list_flavors.body
test 'returns a Hash' do
flavors.is_a? Hash
end
tests('flavor').formats(@flavor_format, false) do
flavors['sizes'].first
end
end
end
end

View file

@ -0,0 +1,24 @@
Shindo.tests('Fog::Compute[:digitalocean] | list_images request', ['digitalocean', 'compute']) do
# {"id"=>1601, "name"=>"CentOS 5.8 x64", "distribution"=>"CentOS"}
@image_format = {
'id' => Integer,
'name' => String,
'distribution' => String
}
tests('success') do
tests('#list_images') do
images = Fog::Compute[:digitalocean].list_images.body
test 'returns a Hash' do
images.is_a? Hash
end
tests('image').formats(@image_format, false) do
images['images'].first
end
end
end
end

View file

@ -0,0 +1,23 @@
Shindo.tests('Fog::Compute[:digitalocean] | list_regions request', ['digitalocean', 'compute']) do
# {"id":2,"name":"Amsterdam 1"}
@region_format = {
'id' => Integer,
'name' => String,
}
tests('success') do
tests('#list_regions') do
regions = Fog::Compute[:digitalocean].list_regions.body
test 'returns a Hash' do
regions.is_a? Hash
end
tests('region').formats(@region_format, false) do
regions['regions'].first
end
end
end
end

View file

@ -0,0 +1,26 @@
Shindo.tests('Fog::Compute[:digitalocean] | list_servers request', ['digitalocean', 'compute']) do
@server_format = {
'id' => Integer,
'name' => String,
'image_id' => Integer,
'size_id' => Integer,
'region_id' => Integer,
'backups_active' => Fog::Nullable::Boolean,
'ip_address' => Fog::Nullable::String,
'status' => String
}
tests('success') do
tests('#list_servers') do
Fog::Compute[:digitalocean].list_servers.body['droplets'].each do |server|
tests('format').data_matches_schema(@server_format) do
server
end
end
end
end
end

View file

@ -0,0 +1,21 @@
Shindo.tests('Fog::Compute[:digitalocean] | list_ssh_keys request', ['digitalocean', 'compute']) do
@ssh_key_format = {
'id' => Integer,
'name' => String
}
tests('success') do
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|
tests('format').data_matches_schema(@ssh_key_format) do
key
end
end
end
end
end

View file

@ -0,0 +1,20 @@
Shindo.tests('Fog::Compute[:digitalocean] | power_cycle_server request', ['digitalocean', 'compute']) do
server = fog_test_server
tests('success') do
tests('#power_cycle_server') do
test('returns 200') do
service.power_cycle_server(server.id).status == 200
end
test('state is off') do
server.wait_for { server.state == 'off' }
server.state == 'off'
end
end
end
end

View file

@ -0,0 +1,25 @@
Shindo.tests('Fog::Compute[:digitalocean] | power on/off/shutdown requests',
['digitalocean', 'compute']) do
service = Fog::Compute[:digitalocean]
server = fog_test_server
tests('success') do
test('#power_off_server') do
service.power_off_server(server.id).body['status'] == 'OK'
end
test('#power_on_server') do
service.power_on_server(server.id).body['status'] == 'OK'
end
test('#shutdown_server') do
service.shutdown_server(server.id).body['status'] == 'OK'
end
server.start
end
end

View file

@ -0,0 +1,14 @@
Shindo.tests('Fog::Compute[:digitalocean] | reboot_server request', ['digitalocean', 'compute']) do
server = fog_test_server
tests('success') do
tests('#reboot_server').succeeds do
service.reboot_server(server.id).body['status'] == 'OK'
end
end
end