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 reauth

This commit is contained in:
Kyle Rames 2013-07-01 10:23:42 -05:00
commit 553f848256
70 changed files with 1437 additions and 454 deletions

View file

@ -6,7 +6,7 @@ rvm:
- 1.9.3
- 2.0.0
script: FOG_MOCK=true bundle exec shindont
script: bundle exec rake travis
notifications:
email: false

View file

@ -1,3 +1,8 @@
source "https://rubygems.org"
group :development, :test do
# This is here because gemspec doesn't support require: false
gem 'coveralls', :require => false
end
gemspec

View file

@ -47,20 +47,21 @@ end
GEM_NAME = "#{name}"
task :default => :test
task :travis => ['test:travis', 'coveralls_push_workaround']
require "tasks/test_task"
Fog::Rake::TestTask.new
namespace :test do
mock = 'true' || ENV['FOG_MOCK']
task :travis do
sh("export FOG_MOCK=#{mock} && bundle exec shindont")
end
task :vsphere do
[true].each do |mock|
sh("export FOG_MOCK=#{mock} && bundle exec shindont tests/vsphere")
end
end
task :openvz do
[true].each do |mock|
sh("export FOG_MOCK=#{mock} && bundle exec shindont tests/openvz")
end
end
end
@ -186,3 +187,12 @@ end
require "tasks/changelog_task"
Fog::Rake::ChangelogTask.new
task :coveralls_push_workaround do
ENV['COVERAGE'] = 'false' if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('1.9')
unless ENV['COVERAGE'] == 'false'
require 'coveralls/rake/task'
Coveralls::RakeTask.new
Rake::Task["coveralls:push"].invoke
end
end

22
bin/fog
View file

@ -1,7 +1,29 @@
#!/usr/bin/env ruby
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'fog'))
require 'optparse'
require 'irb'
require 'yaml'
options = OptionParser.new do |opts|
opts.banner = 'usage: fog [options] CREDENTIAL'
opts.on('-C', '--credentials-path FILE', 'Path to the credentials file') do |file|
Fog.credentials_path = file
end
opts.on_tail('-V', '--version', 'Prints the version') do
puts Fog::VERSION
exit
end
opts.on_tail('-h', '--help', 'Prints this message') do
puts opts
exit
end
end
options.parse!
Fog.credential = ARGV.first ? ARGV.first.to_sym : nil
Fog.mock! if ENV['FOG_MOCK']
if Fog.credentials.empty?

View file

@ -41,7 +41,7 @@ Gem::Specification.new do |s|
## List your runtime dependencies here. Runtime dependencies are those
## that are needed for an end user to actually USE your code.
s.add_dependency('builder')
s.add_dependency('excon', '~>0.24.0')
s.add_dependency('excon', '~>0.25.0')
s.add_dependency('formatador', '~>0.2.0')
s.add_dependency('multi_json', '~>1.0')
s.add_dependency('mime-types')

View file

@ -1,4 +1,5 @@
require 'fog/core/json'
require "fog/json"
module Fog
module AWS
module CredentialFetcher
@ -14,7 +15,7 @@ module Fog
session = Fog::JSON.decode(role_data)
credentials = {}
credentials[:aws_access_key_id] = session['AccessKeyId']
credentials[:aws_access_key_id] = session['AccessKeyId']
credentials[:aws_secret_access_key] = session['SecretAccessKey']
credentials[:aws_session_token] = session['Token']
credentials[:aws_credentials_expire_at] = Time.xmlschema session['Expiration']
@ -31,7 +32,7 @@ module Fog
end
module ConnectionMethods
def refresh_credentials_if_expired
refresh_credentials if credentials_expired?
end
@ -39,8 +40,8 @@ module Fog
private
def credentials_expired?
@use_iam_profile &&
(!@aws_credentials_expire_at ||
@use_iam_profile &&
(!@aws_credentials_expire_at ||
(@aws_credentials_expire_at && Fog::Time.now > @aws_credentials_expire_at - 15)) #new credentials become available from around 5 minutes before expiration time
end
@ -61,4 +62,3 @@ module Fog
end
end
end

View file

@ -1,14 +1,12 @@
require 'fog/brightbox'
require 'fog/compute'
require 'fog/brightbox/oauth2'
require 'fog/brightbox/compute/shared'
require 'fog/brightbox/compute/image_selector'
module Fog
module Compute
class Brightbox < Fog::Service
API_URL = "https://api.gb1.brightbox.com/"
# Client credentials
requires :brightbox_client_id, :brightbox_secret
@ -34,6 +32,8 @@ module Fog
model :application
collection :api_clients
model :api_client
collection :collaborations
model :collaboration
collection :servers
model :server
collection :server_groups
@ -54,17 +54,22 @@ module Fog
model :cloud_ip
collection :users
model :user
collection :user_collaborations
model :user_collaboration
request_path 'fog/brightbox/requests/compute'
request :accept_user_collaboration
request :activate_console_server
request :add_listeners_load_balancer
request :add_nodes_load_balancer
request :add_servers_server_group
request :apply_to_firewall_policy
request :accept_user_collaboration
request :remove_firewall_policy
request :create_api_client
request :create_application
request :create_cloud_ip
request :create_collaboration
request :create_firewall_policy
request :create_firewall_rule
request :create_image
@ -74,17 +79,20 @@ module Fog
request :delete_api_client
request :delete_application
request :delete_cloud_ip
request :delete_collaboration
request :delete_firewall_policy
request :delete_firewall_rule
request :delete_image
request :delete_load_balancer
request :delete_server
request :delete_server_group
request :delete_user_collaboration
request :get_account
request :get_api_client
request :get_application
request :get_authenticated_user
request :get_cloud_ip
request :get_collaboration
request :get_firewall_policy
request :get_firewall_rule
request :get_image
@ -95,11 +103,13 @@ module Fog
request :get_server_group
request :get_server_type
request :get_user
request :get_user_collaboration
request :get_zone
request :list_accounts
request :list_api_clients
request :list_applications
request :list_cloud_ips
request :list_collaborations
request :list_firewall_policies
request :list_images
request :list_load_balancers
@ -107,16 +117,21 @@ module Fog
request :list_server_types
request :list_servers
request :list_users
request :list_user_collaborations
request :list_zones
request :map_cloud_ip
request :move_servers_server_group
request :reject_user_collaboration
request :remove_listeners_load_balancer
request :remove_nodes_load_balancer
request :remove_servers_server_group
request :resend_collaboration
request :reset_ftp_password_account
request :reset_ftp_password_scoped_account
request :reset_secret_api_client
request :reset_secret_application
request :resend_collaboration
request :reject_user_collaboration
request :shutdown_server
request :snapshot_server
request :start_server
@ -135,220 +150,13 @@ module Fog
request :update_server_group
request :update_user
module Shared
include Fog::Brightbox::OAuth2
# Creates a new instance of the Brightbox Compute service
#
# @note If you create service using just a refresh token when it
# expires the service will no longer be able to authenticate.
#
# @param [Hash] options
# @option options [String] :brightbox_api_url Override the default (or configured) API endpoint
# @option options [String] :brightbox_auth_url Override the default (or configured) API authentication endpoint
# @option options [String] :brightbox_client_id Client identifier to authenticate with (overrides configured)
# @option options [String] :brightbox_secret Client secret to authenticate with (overrides configured)
# @option options [String] :brightbox_username Email or user identifier for user based authentication
# @option options [String] :brightbox_password Password for user based authentication
# @option options [String] :brightbox_account Account identifier to scope this connection to
# @option options [String] :connection_options Settings to pass to underlying {Fog::Connection}
# @option options [Boolean] :persistent Sets a persistent HTTP {Fog::Connection}
# @option options [String] :brightbox_access_token Sets the OAuth access token to use rather than requesting a new token
# @option options [String] :brightbox_refresh_token Sets the refresh token to use when requesting a newer access token
# @option options [String] :brightbox_token_management Overide the existing behaviour to request access tokens if expired (default is `true`)
#
def initialize(options)
# Currently authentication and api endpoints are the same but may change
@auth_url = options[:brightbox_auth_url] || Fog.credentials[:brightbox_auth_url] || API_URL
@auth_connection = Fog::Connection.new(@auth_url)
@api_url = options[:brightbox_api_url] || Fog.credentials[:brightbox_api_url] || API_URL
@connection_options = options[:connection_options] || {}
@persistent = options[:persistent] || false
@connection = Fog::Connection.new(@api_url, @persistent, @connection_options)
# Authentication options
client_id = options[:brightbox_client_id] || Fog.credentials[:brightbox_client_id]
client_secret = options[:brightbox_secret] || Fog.credentials[:brightbox_secret]
username = options[:brightbox_username] || Fog.credentials[:brightbox_username]
password = options[:brightbox_password] || Fog.credentials[:brightbox_password]
@configured_account = options[:brightbox_account] || Fog.credentials[:brightbox_account]
# Request account can be changed at anytime and changes behaviour of future requests
@scoped_account = @configured_account
credential_options = {:username => username, :password => password}
@credentials = CredentialSet.new(client_id, client_secret, credential_options)
# If existing tokens have been cached, allow continued use of them in the service
@credentials.update_tokens(options[:brightbox_access_token], options[:brightbox_refresh_token])
@token_management = options.fetch(:brightbox_token_management, true)
end
# Sets the scoped account for future requests
# @param [String] scoped_account Identifier of the account to scope request to
def scoped_account=(scoped_account)
@scoped_account = scoped_account
end
# This returns the account identifier that the request should be scoped by
# based on the options passed to the request and current configuration
#
# @param [String] options_account Any identifier passed into the request
#
# @return [String, nil] The account identifier to scope the request to or nil
def scoped_account(options_account = nil)
[options_account, @scoped_account].compact.first
end
# Resets the scoped account back to intially configured one
def scoped_account_reset
@scoped_account = @configured_account
end
# Returns the scoped account being used for requests
#
# * For API clients this is the owning account
# * For User applications this is the account specified by either +account_id+
# option on the service or the +brightbox_account+ setting in your configuration
#
# @return [Fog::Compute::Brightbox::Account]
#
def account
account_data = get_scoped_account.merge(:service => self)
Fog::Compute::Brightbox::Account.new(account_data)
end
# Returns true if authentication is being performed as a user
# @return [Boolean]
def authenticating_as_user?
@credentials.user_details?
end
# Returns true if an access token is set
# @return [Boolean]
def access_token_available?
!! @credentials.access_token
end
# Returns the current access token or nil
# @return [String,nil]
def access_token
@credentials.access_token
end
# Returns the current refresh token or nil
# @return [String,nil]
def refresh_token
@credentials.refresh_token
end
# Returns the current token expiry time in seconds or nil
# @return [Number,nil]
def expires_in
@credentials.expires_in
end
# Requests a new access token
#
# @return [String] New access token
def get_access_token
begin
get_access_token!
rescue Excon::Errors::Unauthorized, Excon::Errors::BadRequest
@credentials.update_tokens(nil, nil)
end
@credentials.access_token
end
# Requests a new access token and raises if there is a problem
#
# @return [String] New access token
# @raise [Excon::Errors::BadRequest] The credentials are expired or incorrect
#
def get_access_token!
response = request_access_token(@auth_connection, @credentials)
update_credentials_from_response(@credentials, response)
@credentials.access_token
end
# Returns an identifier for the default image for use
#
# Currently tries to find the latest version of Ubuntu (i686) from
# Brightbox.
#
# Highly recommended that you actually select the image you want to run
# on your servers yourself!
#
# @return [String] if image is found, returns the identifier
# @return [NilClass] if no image is found or an error occurs
#
def default_image
return @default_image_id unless @default_image_id.nil?
@default_image_id = Fog.credentials[:brightbox_default_image] || select_default_image
end
private
# This makes a request of the API based on the configured setting for
# token management.
#
# @param [Hash] options Excon compatible options
# @see https://github.com/geemus/excon/blob/master/lib/excon/connection.rb
#
# @return [Hash] Data of response body
#
def make_request(options)
if @token_management
managed_token_request(options)
else
authenticated_request(options)
end
end
# This request checks for access tokens and will ask for a new one if
# it receives Unauthorized from the API before repeating the request
#
# @param [Hash] options Excon compatible options
#
# @return [Excon::Response]
def managed_token_request(options)
begin
get_access_token unless access_token_available?
response = authenticated_request(options)
rescue Excon::Errors::Unauthorized
get_access_token
response = authenticated_request(options)
end
end
# This request makes an authenticated request of the API using currently
# setup credentials.
#
# @param [Hash] options Excon compatible options
#
# @see https://github.com/geemus/excon/blob/master/lib/excon/connection.rb
#
# @return [Excon::Response]
def authenticated_request(options)
headers = options[:headers] || {}
headers.merge!("Authorization" => "OAuth #{@credentials.access_token}", "Content-Type" => "application/json")
options[:headers] = headers
# TODO This is just a wrapper around a call to Excon::Connection#request
# so can be extracted from Compute by passing in the connection,
# credentials and options
@connection.request(options)
end
end
# The Mock Service allows you to run a fake instance of the Service
# which makes no real connections.
#
# @todo Implement
#
class Mock
include Shared
include Fog::Brightbox::Compute::Shared
def request(method, path, expected_responses, parameters = {})
_request
@ -373,7 +181,7 @@ module Fog
# service.
#
class Real
include Shared
include Fog::Brightbox::Compute::Shared
# Makes an API request to the given path using passed options or those
# set with the service setup

View file

@ -0,0 +1,232 @@
require "fog/brightbox/oauth2"
module Fog
module Brightbox
module Compute
# The Shared module consists of code that was duplicated between the Real
# and Mock implementations.
#
module Shared
include Fog::Brightbox::OAuth2
API_URL = "https://api.gb1.brightbox.com/"
# Creates a new instance of the Brightbox Compute service
#
# @note If you create service using just a refresh token when it
# expires the service will no longer be able to authenticate.
#
# @param [Hash] options
# @option options [String] :brightbox_api_url
# Override the default (or configured) API endpoint
# @option options [String] :brightbox_auth_url
# Override the default (or configured) API authentication endpoint
# @option options [String] :brightbox_client_id
# Client identifier to authenticate with (overrides configured)
# @option options [String] :brightbox_secret
# Client secret to authenticate with (overrides configured)
# @option options [String] :brightbox_username
# Email or user identifier for user based authentication
# @option options [String] :brightbox_password
# Password for user based authentication
# @option options [String] :brightbox_account
# Account identifier to scope this connection to
# @option options [String] :connection_options
# Settings to pass to underlying {Fog::Connection}
# @option options [Boolean] :persistent
# Sets a persistent HTTP {Fog::Connection}
# @option options [String] :brightbox_access_token
# Sets the OAuth access token to use rather than requesting a new token
# @option options [String] :brightbox_refresh_token
# Sets the refresh token to use when requesting a newer access token
# @option options [String] (true) :brightbox_token_management
# Overide the existing behaviour to request access tokens if expired
#
def initialize(options)
# Currently authentication and api endpoints are the same but may change
@auth_url = options[:brightbox_auth_url] || Fog.credentials[:brightbox_auth_url] || API_URL
@auth_connection = Fog::Connection.new(@auth_url)
@api_url = options[:brightbox_api_url] || Fog.credentials[:brightbox_api_url] || API_URL
@connection_options = options[:connection_options] || {}
@persistent = options[:persistent] || false
@connection = Fog::Connection.new(@api_url, @persistent, @connection_options)
# Authentication options
client_id = options[:brightbox_client_id] || Fog.credentials[:brightbox_client_id]
client_secret = options[:brightbox_secret] || Fog.credentials[:brightbox_secret]
username = options[:brightbox_username] || Fog.credentials[:brightbox_username]
password = options[:brightbox_password] || Fog.credentials[:brightbox_password]
@configured_account = options[:brightbox_account] || Fog.credentials[:brightbox_account]
# Request account can be changed at anytime and changes behaviour of future requests
@scoped_account = @configured_account
credential_options = {:username => username, :password => password}
@credentials = CredentialSet.new(client_id, client_secret, credential_options)
# If existing tokens have been cached, allow continued use of them in the service
@credentials.update_tokens(options[:brightbox_access_token], options[:brightbox_refresh_token])
@token_management = options.fetch(:brightbox_token_management, true)
end
# Sets the scoped account for future requests
# @param [String] scoped_account Identifier of the account to scope request to
def scoped_account=(scoped_account)
@scoped_account = scoped_account
end
# This returns the account identifier that the request should be scoped by
# based on the options passed to the request and current configuration
#
# @param [String] options_account Any identifier passed into the request
#
# @return [String, nil] The account identifier to scope the request to or nil
def scoped_account(options_account = nil)
[options_account, @scoped_account].compact.first
end
# Resets the scoped account back to intially configured one
def scoped_account_reset
@scoped_account = @configured_account
end
# Returns the scoped account being used for requests
#
# * For API clients this is the owning account
# * For User applications this is the account specified by either +account_id+
# option on the service or the +brightbox_account+ setting in your configuration
#
# @return [Fog::Compute::Brightbox::Account]
#
def account
account_data = get_scoped_account.merge(:service => self)
Fog::Compute::Brightbox::Account.new(account_data)
end
# Returns true if authentication is being performed as a user
# @return [Boolean]
def authenticating_as_user?
@credentials.user_details?
end
# Returns true if an access token is set
# @return [Boolean]
def access_token_available?
!! @credentials.access_token
end
# Returns the current access token or nil
# @return [String,nil]
def access_token
@credentials.access_token
end
# Returns the current refresh token or nil
# @return [String,nil]
def refresh_token
@credentials.refresh_token
end
# Returns the current token expiry time in seconds or nil
# @return [Number,nil]
def expires_in
@credentials.expires_in
end
# Requests a new access token
#
# @return [String] New access token
def get_access_token
begin
get_access_token!
rescue Excon::Errors::Unauthorized, Excon::Errors::BadRequest
@credentials.update_tokens(nil, nil)
end
@credentials.access_token
end
# Requests a new access token and raises if there is a problem
#
# @return [String] New access token
# @raise [Excon::Errors::BadRequest] The credentials are expired or incorrect
#
def get_access_token!
response = request_access_token(@auth_connection, @credentials)
update_credentials_from_response(@credentials, response)
@credentials.access_token
end
# Returns an identifier for the default image for use
#
# Currently tries to find the latest version of Ubuntu (i686) from
# Brightbox.
#
# Highly recommended that you actually select the image you want to run
# on your servers yourself!
#
# @return [String] if image is found, returns the identifier
# @return [NilClass] if no image is found or an error occurs
#
def default_image
return @default_image_id unless @default_image_id.nil?
@default_image_id = Fog.credentials[:brightbox_default_image] || select_default_image
end
private
# This makes a request of the API based on the configured setting for
# token management.
#
# @param [Hash] options Excon compatible options
# @see https://github.com/geemus/excon/blob/master/lib/excon/connection.rb
#
# @return [Hash] Data of response body
#
def make_request(options)
if @token_management
managed_token_request(options)
else
authenticated_request(options)
end
end
# This request checks for access tokens and will ask for a new one if
# it receives Unauthorized from the API before repeating the request
#
# @param [Hash] options Excon compatible options
#
# @return [Excon::Response]
def managed_token_request(options)
begin
get_access_token unless access_token_available?
response = authenticated_request(options)
rescue Excon::Errors::Unauthorized
get_access_token
response = authenticated_request(options)
end
end
# This request makes an authenticated request of the API using currently
# setup credentials.
#
# @param [Hash] options Excon compatible options
#
# @see https://github.com/geemus/excon/blob/master/lib/excon/connection.rb
#
# @return [Excon::Response]
def authenticated_request(options)
headers = options[:headers] || {}
headers.merge!("Authorization" => "OAuth #{@credentials.access_token}", "Content-Type" => "application/json")
options[:headers] = headers
# TODO This is just a wrapper around a call to Excon::Connection#request
# so can be extracted from Compute by passing in the connection,
# credentials and options
@connection.request(options)
end
end
end
end
end

View file

@ -0,0 +1,43 @@
require 'fog/core/model'
module Fog
module Compute
class Brightbox
class Collaboration < Fog::Model
identity :id
attribute :status
attribute :email
attribute :role
attribute :role_label
attribute :account
attribute :user
attribute :inviter
def account_id
account['id'] || account[:id]
end
def save
raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if identity
options = {
:role => role,
:email => email
}.delete_if { |k, v| v.nil? || v == "" }
data = connection.create_collaboration(options)
merge_attributes(data)
true
end
def destroy
requires :identity
connection.destroy_collaboration(identity)
true
end
end
end
end
end

View file

@ -0,0 +1,23 @@
require "fog/core/collection"
require "fog/brightbox/models/compute/collaboration"
module Fog
module Compute
class Brightbox
class Collaborations < Fog::Collection
model Fog::Compute::Brightbox::Collaboration
def all
data = connection.list_collaborations
load(data)
end
def destroy
requires :identity
connection.destroy_collaboration(identity)
true
end
end
end
end
end

View file

@ -0,0 +1,29 @@
require 'fog/core/model'
module Fog
module Compute
class Brightbox
class UserCollaboration < Fog::Model
identity :id
attribute :status
attribute :email
attribute :role
attribute :role_label
attribute :account
attribute :user
attribute :inviter
def account_id
account['id'] || account[:id]
end
def destroy
requires :identity
connection.destroy_user_collaboration(identity)
true
end
end
end
end
end

View file

@ -0,0 +1,23 @@
require "fog/core/collection"
require "fog/brightbox/models/compute/user_collaboration"
module Fog
module Compute
class Brightbox
class UserCollaborations < Fog::Collection
model Fog::Compute::Brightbox::UserCollaboration
def all
data = connection.list_user_collaborations
load(data)
end
def destroy
requires :identity
connection.destroy_user_collaboration(identity)
true
end
end
end
end
end

View file

@ -0,0 +1,21 @@
module Fog
module Compute
class Brightbox
class Real
# Accepts the collaboration and gaining permitted access
#
# @param [String] identifier Unique reference to identify the resource
#
# @return [Hash] if successful Hash version of JSON object
#
# @see https://api.gb1.brightbox.com/1.0/#user_collaboration_accept_user_collaboration
#
def accept_user_collaboration(identifier)
return nil if identifier.nil? || identifier == ""
wrapped_request("post", "/1.0/user/collaborations/#{identifier}/accept", [200])
end
end
end
end
end

View file

@ -0,0 +1,23 @@
module Fog
module Compute
class Brightbox
class Real
# Creates a new collaboration for a user for the account
#
# @param [Hash] options
# @option options [String] :email Email address of user to invite
# @option options [String] :role Role to grant to the user. Currently only `admin`
#
# @return [Hash] if successful Hash version of JSON object
# @return [NilClass] if no options were passed
#
# @see https://api.gb1.brightbox.com/1.0/#collaboration_create_collaboration
#
def create_collaboration(options)
wrapped_request("post", "/1.0/collaborations", [201], options)
end
end
end
end
end

View file

@ -0,0 +1,28 @@
module Fog
module Compute
class Brightbox
class Real
# Cancels or completes the collaboration
#
# @param [String] identifier Unique reference to identify the resource
#
# @return [Hash] if successful Hash version of JSON object
#
# @see https://api.gb1.brightbox.com/1.0/#collaboration_delete_collaboration
#
def delete_collaboration(identifier)
return nil if identifier.nil? || identifier == ""
wrapped_request("delete", "/1.0/collaborations/#{identifier}", [200])
end
# Old format of the delete request.
#
# @deprecated Use +#delete_collaboration+ instead
#
def destroy_collaboration(identifier)
delete_collaboration(identifier)
end
end
end
end
end

View file

@ -0,0 +1,28 @@
module Fog
module Compute
class Brightbox
class Real
# Ends an existing 'accepted' collaboration
#
# @param [String] identifier Unique reference to identify the resource
#
# @return [Hash] if successful Hash version of JSON object
#
# @see https://api.gb1.brightbox.com/1.0/#user_collaboration_delete_user_collaboration
#
def delete_user_collaboration(identifier)
return nil if identifier.nil? || identifier == ""
wrapped_request("delete", "/1.0/user/collaborations/#{identifier}", [200])
end
# Old format of the delete request.
#
# @deprecated Use +#delete_user_collaboration+ instead
#
def destroy_user_collaboration(identifier)
delete_user_collaboration(identifier)
end
end
end
end
end

View file

@ -0,0 +1,21 @@
module Fog
module Compute
class Brightbox
class Real
# Ends an existing 'accepted' collaboration
#
# @param [String] identifier Unique reference to identify the resource
#
# @return [Hash] The JSON response parsed to a Hash
#
# @see https://api.gb1.brightbox.com/1.0/#user_collaboration_destroy_user_collaboration
#
def destroy_user_collaboration(identifier)
return nil if identifier.nil? || identifier == ""
wrapped_request("delete", "/1.0/user/collaborations/#{identifier}", [200])
end
end
end
end
end

View file

@ -0,0 +1,21 @@
module Fog
module Compute
class Brightbox
class Real
# Shows details of one collaboration
#
# @param [String] identifier Unique reference to identify the resource
#
# @return [Hash] if successful Hash version of JSON object
#
# @see https://api.gb1.brightbox.com/1.0/#collaboration_get_collaboration
#
def get_collaboration(identifier)
return nil if identifier.nil? || identifier == ""
wrapped_request("get", "/1.0/collaborations/#{identifier}", [200])
end
end
end
end
end

View file

@ -0,0 +1,21 @@
module Fog
module Compute
class Brightbox
class Real
# Shows details of one collaboration
#
# @param [String] identifier Unique reference to identify the resource
#
# @return [Hash] if successful Hash version of JSON object
#
# @see https://api.gb1.brightbox.com/1.0/#user_collaboration_get_user_collaboration
#
def get_user_collaboration(identifier)
return nil if identifier.nil? || identifier == ""
wrapped_request("get", "/1.0/user/collaborations/#{identifier}", [200])
end
end
end
end
end

View file

@ -0,0 +1,19 @@
module Fog
module Compute
class Brightbox
class Real
# Lists all the account collaborations
#
#
# @return [Hash] if successful Hash version of JSON object
#
# @see https://api.gb1.brightbox.com/1.0/#collaboration_list_collaborations
#
def list_collaborations
wrapped_request("get", "/1.0/collaborations", [200])
end
end
end
end
end

View file

@ -0,0 +1,19 @@
module Fog
module Compute
class Brightbox
class Real
# Lists all collaborations the user is involved with
#
#
# @return [Hash] if successful Hash version of JSON object
#
# @see https://api.gb1.brightbox.com/1.0/#user_collaboration_list_user_collaborations
#
def list_user_collaborations
wrapped_request("get", "/1.0/user/collaborations", [200])
end
end
end
end
end

View file

@ -0,0 +1,21 @@
module Fog
module Compute
class Brightbox
class Real
# Rejects the collaboration and removes the offer
#
# @param [String] identifier Unique reference to identify the resource
#
# @return [Hash] if successful Hash version of JSON object
#
# @see https://api.gb1.brightbox.com/1.0/#user_collaboration_reject_user_collaboration
#
def reject_user_collaboration(identifier)
return nil if identifier.nil? || identifier == ""
wrapped_request("post", "/1.0/user/collaborations/#{identifier}/reject", [200])
end
end
end
end
end

View file

@ -0,0 +1,21 @@
module Fog
module Compute
class Brightbox
class Real
# Resends the invitation email to the collaborator
#
# @param [String] identifier Unique reference to identify the resource
#
# @return [Hash] if successful Hash version of JSON object
#
# @see https://api.gb1.brightbox.com/1.0/#collaboration_resend_collaboration
#
def resend_collaboration(identifier)
return nil if identifier.nil? || identifier == ""
wrapped_request("post", "/1.0/collaborations/#{identifier}/resend", [200])
end
end
end
end
end

View file

@ -0,0 +1,59 @@
# Getting Started with Fog on CloudSigma
## Requirements
In order to use CloudSigma with Fog, you must use Fog version 1.12.0 or later.
## Setting credentials
Fog uses `~/.fog` to store credentials. To add CloudSigma as your default provider, simply add the following:
:default:
:cloudsigma_username: user@example.com
:cloudsigma_password: SomeRandomPassword
:cloudsigma_host: zrh.cloudsigma.com
Please note that you need to specify the host. If you're on the Zurich-based cloud, you will need to enter `zrh.cloudsigma.com` and if you're on the Las Vegas cloud, you'll need to enter `lvs.cloudsigma.com`.
## Creating a server
You can of course interact with Fog directly from your Ruby application, but in this example, we'll simply use the `fog` CLI tool. In the example below, we'll first create a 5GB disk, then we create server with 2Ghz CPU and 2GB RAM. Finally we attach the drive and boot up the server.
$ fog
> cs = Compute[:CloudSigma]
> drive = cs.volumes.create(:name => 'fog_drive', :size => '5368709120', :media => 'disk')
> server = cs.servers.create(:name => 'fog_server', :cpu => '2000', :mem => '2147483648', :vnc_password => 'foobar')
> server.mount_volume(drive.uuid)
> server.update
> server.start
Now, this wasn't very useful by itself since the drive we created was just a blank drive (as a result it cannot boot). It does however illustrate a minimal work flow.
To make this a bit more useful, let's try to attach an ISO image (in this case Ubuntu 12.04 LTS), and boot into the installer. To do this, we'll run the following commands (assuming you haven't closed the session from above). You can either upload your own installer image, or you can use one from the drives library. In either case, you need to pass the UUID for the drive.
> server.stop
> ubuntu_image_uuid = '41d848c2-44e4-4428-9406-84e95bb1288d'
> server.unmount(drive.uuid)
> server.mount_volume(ubuntu_image_uuid, 'ide', '0:0', 1)
> server.mount_volume(drive.uuid, 'virtio', '0:0', 2)
> server.update
> server.start
What this does is to stop the server, unmount the previous drive, then we attach the Ubuntu installation drive as an IDE device (on bus 0:0), with the boot order 1 (first). We then mount the system drive as Virtio device (on bus 0:0) with the boot order 2. Finally we push the changes to the server and start it. This will bring you into the Ubuntu installation.
In order to actually run the installer, you need to open a VNC session to the server. This can be done bye issue the following command:
> server.open_vnc
That will print out the VNC URL, among with other data. You can simply pass the value of 'vnc_url' into your VNC client. When opening the session, you also need to provide the password, which we set to 'foobar' during the server creation.
After you're done with the installation, you can unmount the Ubuntu installation disk by running the following command:
> server.unmount(ubuntu_image_uuid)
You might also want to close the VNC session to increase security. This can be done by running:
> server.close_vnc
That's it. You've now set up a fully working Ubuntu server on CloudSigma using fog.

View file

@ -19,11 +19,9 @@ require 'fog/core/current_machine'
require 'fog/core/deprecation'
require 'fog/core/errors'
require 'fog/core/hmac'
require 'fog/core/json'
require 'fog/core/logger'
require 'fog/core/model'
require 'fog/core/mock'
require 'fog/core/parser' # FIXME: would be better to only load when nokogiri is required
require 'fog/core/provider'
require 'fog/core/service'
require 'fog/core/ssh'
@ -32,6 +30,13 @@ require 'fog/core/time'
require 'fog/core/timeout'
require 'fog/core/wait_for'
# data exchange specific (to be extracted and used on a per provider basis)
require 'fog/xml'
require 'fog/json'
# deprecation wrappers
require 'fog/core/deprecated/connection'
# service wrappers
require 'fog/compute'
require 'fog/identity'

View file

@ -1,40 +1,73 @@
module Fog
class Connection
module Core
def initialize(url, persistent=false, params={})
unless params.has_key?(:debug_response)
params[:debug_response] = true
end
params[:headers] ||= {}
params[:headers]['User-Agent'] ||= "fog/#{Fog::VERSION}"
@excon = Excon.new(url, params)
@persistent = persistent
end
def request(params, &block)
unless @persistent
reset
end
unless block_given?
if (parser = params.delete(:parser))
body = Nokogiri::XML::SAX::PushParser.new(parser)
params[:response_block] = lambda { |chunk, remaining, total| body << chunk }
# Fog::Core::Connection is a generic class to contain a HTTP link to an API.
#
# It is intended to be subclassed by providers who can then add their own
# modifications such as authentication or response object.
#
class Connection
# Prepares the connection and sets defaults for any future requests.
#
# @param [String] url The destination URL
# @param persistent [Boolean]
# @param [Hash] params
# @option params [String] :body Default text to be sent over a socket. Only used if :body absent in Connection#request params
# @option params [Hash<Symbol, String>] :headers The default headers to supply in a request. Only used if params[:headers] is not supplied to Connection#request
# @option params [String] :host The destination host's reachable DNS name or IP, in the form of a String
# @option params [String] :path Default path; appears after 'scheme://host:port/'. Only used if params[:path] is not supplied to Connection#request
# @option params [Fixnum] :port The port on which to connect, to the destination host
# @option params [Hash] :query Default query; appended to the 'scheme://host:port/path/' in the form of '?key=value'. Will only be used if params[:query] is not supplied to Connection#request
# @option params [String] :scheme The protocol; 'https' causes OpenSSL to be used
# @option params [String] :proxy Proxy server; e.g. 'http://myproxy.com:8888'
# @option params [Fixnum] :retry_limit Set how many times we'll retry a failed request. (Default 4)
# @option params [Class] :instrumentor Responds to #instrument as in ActiveSupport::Notifications
# @option params [String] :instrumentor_name Name prefix for #instrument events. Defaults to 'excon'
#
def initialize(url, persistent=false, params={})
unless params.has_key?(:debug_response)
params[:debug_response] = true
end
params[:headers] ||= {}
params[:headers]['User-Agent'] ||= "fog/#{Fog::VERSION}"
@excon = Excon.new(url, params)
@persistent = persistent
end
response = @excon.request(params, &block)
if parser
body.finish
response.body = parser.response
# Makes a request using the connection using Excon
#
# @param [Hash] params
# @option params [String] :body text to be sent over a socket
# @option params [Hash<Symbol, String>] :headers The default headers to supply in a request
# @option params [String] :host The destination host's reachable DNS name or IP, in the form of a String
# @option params [String] :path appears after 'scheme://host:port/'
# @option params [Fixnum] :port The port on which to connect, to the destination host
# @option params [Hash] :query appended to the 'scheme://host:port/path/' in the form of '?key=value'
# @option params [String] :scheme The protocol; 'https' causes OpenSSL to be used
# @option params [Proc] :response_block
#
# @return [Excon::Response]
#
# @raise [Excon::Errors::StubNotFound]
# @raise [Excon::Errors::Timeout]
# @raise [Excon::Errors::SocketError]
#
def request(params, &block)
reset unless @persistent
@excon.request(params, &block)
end
response
end
# Make {#request} available even when it has been overidden by a subclass
# to allow backwards compatibility.
#
alias_method :original_request, :request
protected :original_request
def reset
@excon.reset
# Closes the connection
#
def reset
@excon.reset
end
end
end
end

View file

@ -0,0 +1,24 @@
require "fog/xml"
module Fog
# @deprecated Use {Fog::Core::Connection} or {XML::SAXParserConnection} if you
# require the response body to be parsed.
#
# The Connection class is a wrapper around an instance of Excon::Connection
# supporting {#request} and {#reset} only.
#
# {#request} includes an option to perform SAX parsing for XML APIs.
#
# @see https://github.com/geemus/excon/blob/master/lib/excon/connection.rb
#
class Connection < Fog::XML::SAXParserConnection
def request(params, &block)
if (parser = params.delete(:parser))
super(parser, params)
else
original_request(params)
end
end
end
end

View file

@ -1,4 +1,4 @@
require 'nokogiri'
require "nokogiri"
module Fog
module Parsers

View file

@ -41,6 +41,8 @@ module Fog
request :insert_network
request :insert_server
request :set_metadata
model_path 'fog/google/models/compute'
model :server
collection :servers
@ -66,16 +68,15 @@ module Fog
attr_reader :project
def initialize(options)
base_url = 'https://www.googleapis.com/compute/'
api_version = 'v1beta14'
api_version = 'v1beta15'
api_scope_url = 'https://www.googleapis.com/auth/compute'
@project = options[:google_project]
google_client_email = options[:google_client_email]
@api_url = base_url + api_version + '/projects/'
#NOTE: loaded here to avoid requiring this as a core Fog dependency
# NOTE: loaded here to avoid requiring this as a core Fog dependency
begin
require 'google/api_client'
rescue LoadError
@ -87,6 +88,7 @@ module Fog
:application_name => "fog",
:application_version => Fog::VERSION,
})
@client.authorization = Signet::OAuth2::Client.new({
:audience => 'https://accounts.google.com/o/oauth2/token',
:auth_provider_x509_cert_url => "https://www.googleapis.com/oauth2/v1/certs",
@ -96,15 +98,14 @@ module Fog
:signing_key => key,
:token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
})
@client.authorization.fetch_access_token!
@client.authorization.fetch_access_token!
@compute = @client.discovered_api('compute', api_version)
@default_network = 'default'
end
def build_result(api_method, parameters, body_object=nil)
if body_object
#p api_method, parameters
result = @client.execute(
:api_method => api_method,
:parameters => parameters,
@ -123,6 +124,10 @@ module Fog
response.body = Fog::JSON.decode(result.body)
if response.body["error"]
response.status = response.body["error"]["code"]
response.body["error"]["errors"].each do |error|
throw Fog::Errors::Error.new(error["message"])
end
else
response.status = 200
end
@ -131,7 +136,7 @@ module Fog
end
RUNNING_STATE = 'RUNNING'
RUNNING = 'RUNNING'
end
end

View file

@ -12,6 +12,37 @@ module Fog
attribute :creation_timestamp, :aliases => 'creationTimestamp'
attribute :description
attribute :preferred_kernel, :aliases => 'preferredKernel'
attribute :project
def reload
requires :name
data = {}
if project
data = service.get_image(name, project).body
elsif
[ 'google', 'debian-cloud', 'centos-cloud' ].each do |owner|
begin
data = service.get_image(name, owner).body
data[:project] = owner
rescue
end
end
end
self.merge_attributes(data)
self
end
def save
requires :name
reload
end
def resource_url
"#{self.project}/global/images/#{name}"
end
end
end

View file

@ -16,8 +16,8 @@ module Fog
attribute :metadata
def destroy
requires :name
service.delete_server(name)
requires :name, :zone
service.delete_server(name, zone)
end
def image
@ -25,22 +25,37 @@ module Fog
end
def public_ip_address
if self.network_interfaces.count
self.network_interfaces[0]["networkIP"]
else
nil
ip = nil
if self.network_interfaces
self.network_interfaces.each do |netif|
netif["accessConfigs"].each do |access_config|
if access_config["name"] == "External NAT"
ip = access_config['natIP']
end
end
end
end
ip
end
def ready?
data = service.get_server(self.name, self.zone_name).body
data['zone_name'] = self.zone_name
self.merge_attributes(data)
self.state == RUNNING_STATE
self.state == RUNNING
end
def zone
service.get_zone(self.zone_name.split('/')[-1])
if self.zone_name.is_a? String
service.get_zone(self.zone_name.split('/')[-1]).body["name"]
elsif zone_name.is_a? Excon::Response
service.get_zone(zone_name.body["name"]).body["name"]
else
self.zone_name
end
end
def reload
data = service.get_server(self.name, self.zone).body
self.merge_attributes(data)
end
def save
@ -49,29 +64,20 @@ module Fog
requires :machine_type
requires :zone_name
if metadata.nil?
metadata = {}
end
metadata.merge!({
"sshKeys" => "#{username}:#{File.read(public_key_path).strip}"
}) if :public_key_path
data = service.insert_server(
name,
image_name,
zone_name,
machine_type)
data = service.get_server(self.name, self.zone_name).body
service.servers.merge_attributes(data)
end
def setup(credentials = {})
requires :public_ip_address, :public_key, :username
service.set_metadata(self.instance, self.zone, {'sshKeys' => self.public_key })
rescue Errno::ECONNREFUSED
sleep(1)
retry
end
def sshable?(options={})
service.set_metadata(self.instance, self.zone, {'sshKeys' => self.public_key })
ready? && !public_ip_address.nil? && public_key && metadata['sshKeys']
rescue SystemCallError, Net::SSH::AuthenticationFailed, Timeout::Error
false
machine_type,
metadata)
end
end

View file

@ -22,20 +22,20 @@ module Fog
end
def get(identity, zone=nil)
data = nil
response = nil
if zone.nil?
service.list_zones.body['items'].each do |zone|
data = service.get_server(identity, zone['name']).body
break if data["code"] == 200
response = service.get_server(identity, zone['name'])
break if response.status == 200
end
else
data = service.get_server(identity, zone).body
response = service.get_server(identity, zone)
end
if data["code"] != 200
if response.nil? or response.status != 200
nil
else
new(data)
new(response.body)
end
rescue Excon::Errors::NotFound
nil
@ -44,15 +44,17 @@ module Fog
def bootstrap(new_attributes = {})
defaults = {
:name => "fog-#{Time.now.to_i}",
:image_name => "gcel-12-04-v20130225",
:image_name => "debian-7-wheezy-v20130617",
:machine_type => "n1-standard-1",
:zone_name => "us-central1-a",
:private_key_path => File.expand_path("~/.ssh/id_rsa"),
:public_key_path => File.expand_path("~/.ssh/id_rsa.pub"),
:username => ENV['USER'],
}
server = create(defaults.merge(new_attributes))
server.wait_for(Fog.timeout, 30) { ready? }
server.wait_for { sshable? }
server
end
end

View file

@ -15,11 +15,14 @@ module Fog
def delete_server(server_name, zone_name=nil)
if zone_name.nil?
list_zones.body['items'].each do |zone|
data = get_server(server_name, zone['name']).body
if data["error"].nil?
if get_server(server_name, zone['name']).status == 200
zone_name = zone['name']
end
end
else
if zone_name.is_a? Excon::Response
zone_name = zone_name.body["name"]
end
end
api_method = @compute.instances.delete

View file

@ -13,10 +13,16 @@ module Fog
class Real
def get_server(server_name, zone_name)
if zone_name.is_a? Excon::Response
zone = zone_name.body["name"]
else
zone = zone_name
end
api_method = @compute.instances.get
parameters = {
'project' => @project,
'zone' => zone_name,
'zone' => zone,
'instance' => server_name
}

View file

@ -12,16 +12,17 @@ module Fog
class Real
def format_metadata(metadata)
{ "items" => metadata.map {|k,v| {"key" => k, "value" => v}} }
end
def insert_server(server_name, image_name,
zone_name, machine_name,
zone_name, machine_name, metadata,
network_name=@default_network)
# We need to check if the image is owned by the user or a global image.
if get_image(image_name, @project).data['code'] == 200
image_url = @api_url + @project + "/global/images/#{image_name}"
else
image_url = @api_url + "google/global/images/#{image_name}"
end
# We don't know the owner of the image.
image = images.create({:name => image_name})
@image_url = @api_url + image.resource_url
api_method = @compute.instances.insert
parameters = {
@ -30,10 +31,15 @@ module Fog
}
body_object = {
'name' => server_name,
'image' => image_url,
'machineType' => @api_url + @project + "/global/machineTypes/#{machine_name}",
'image' => @image_url,
'machineType' => @api_url + @project + "/zones/#{zone_name}/machineTypes/#{machine_name}",
'metadata' => format_metadata(metadata),
'networkInterfaces' => [{
'network' => @api_url + @project + "/global/networks/#{network_name}"
'network' => @api_url + @project + "/global/networks/#{network_name}",
'accessConfigs' => [{
'type' => 'ONE_TO_ONE_NAT',
'name' => 'External NAT',
}]
}]
}
@ -41,9 +47,7 @@ module Fog
body_object=body_object)
response = self.build_response(result)
end
end
end
end
end

View file

@ -12,10 +12,11 @@ module Fog
class Real
def list_machine_types
def list_machine_types(zone_name)
api_method = @compute.machine_types.list
parameters = {
'project' => 'google'
'project' => @project,
'zone' => zone_name,
}
result = self.build_result(api_method, parameters)

View file

@ -10,8 +10,11 @@ module Fog
recognizes :persistent, :connection_options
recognizes :hp_use_upass_auth_style, :hp_auth_version, :user_agent
recognizes :hp_access_key, :hp_account_id # :hp_account_id is deprecated use hp_access_key instead
# :os_account_meta_temp_url_key is an OpenStack specific setting used to generate temporary urls.
recognizes :os_account_meta_temp_url_key
secrets :hp_secret_key
secrets :hp_secret_key, :os_account_meta_temp_url_key
model_path 'fog/hp/models/storage'
model :directory
@ -167,14 +170,26 @@ module Fog
encoded_path = "#{path}/#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}"
string_to_sign = "#{method}\n#{expires}\n#{sig_path}"
# Only works with 1.9+ Not compatible with 1.8.7
#signed_string = Digest::HMAC.hexdigest(string_to_sign, @hp_secret_key, Digest::SHA1)
# Compatible with 1.8.7 onwards
hmac = OpenSSL::HMAC.new(@hp_secret_key, OpenSSL::Digest::SHA1.new)
signed_string = hmac.update(string_to_sign).hexdigest
signature = @hp_tenant_id.to_s + ":" + @hp_access_key.to_s + ":" + signed_string
signature = Fog::HP.escape(signature)
signature = nil
# HP uses a different strategy to create the signature that is passed to swift than OpenStack.
# As the HP provider is broadly used by OpenStack users the OpenStack strategy is applied when
# the @os_account_meta_temp_url_key is given.
if @os_account_meta_temp_url_key then
hmac = OpenSSL::HMAC.new(@os_account_meta_temp_url_key, OpenSSL::Digest::SHA1.new)
signature= hmac.update(string_to_sign).hexdigest
else
# Only works with 1.9+ Not compatible with 1.8.7
#signed_string = Digest::HMAC.hexdigest(string_to_sign, @hp_secret_key, Digest::SHA1)
# Compatible with 1.8.7 onwards
hmac = OpenSSL::HMAC.new(@hp_secret_key, OpenSSL::Digest::SHA1.new)
signed_string = hmac.update(string_to_sign).hexdigest
signature = @hp_tenant_id.to_s + ":" + @hp_access_key.to_s + ":" + signed_string
signature = Fog::HP.escape(signature)
end
# generate the temp url using the signature and expiry
"#{scheme}://#{host}:#{port}#{encoded_path}?temp_url_sig=#{signature}&temp_url_expires=#{expires}"
@ -217,6 +232,7 @@ module Fog
end
@hp_secret_key = options[:hp_secret_key]
@hp_tenant_id = options[:hp_tenant_id]
@os_account_meta_temp_url_key = options[:os_account_meta_temp_url_key]
end
def data
@ -254,6 +270,7 @@ module Fog
options[:hp_service_type] = "Object Storage"
@hp_tenant_id = options[:hp_tenant_id]
@hp_avl_zone = options[:hp_avl_zone]
@os_account_meta_temp_url_key = options[:os_account_meta_temp_url_key]
### Make the authentication call
if (auth_version == :v2)

View file

@ -1,6 +1,18 @@
require 'multi_json'
require "multi_json"
module Fog
# @note Extracting JSON components out of core is a work in progress.
#
# The {JSON} module includes functionality that is common between APIs using
# JSON to send and receive data.
#
# The intent is to provide common code for provider APIs using JSON but not
# require it for those using XML.
#
# @todo Add +require "fog/json" and/or +include Fog::JSON+ to providers using
# its services
#
module JSON
def self.sanitize(data)
@ -28,6 +40,5 @@ module Fog
def self.decode(obj)
MultiJson.decode(obj)
end
end
end

View file

@ -14,7 +14,7 @@ module Fog
network_id = Fog::Rackspace::MockData.uuid
flavor = {
"OS-FLV-DISABLED:disabled" => false,
"OS-FLV-EXT-DATA:ephemeral" => 4,
"disk" => 20,
"id" => flavor_id,
"links" => [
@ -89,8 +89,8 @@ module Fog
}
#Block Storage
volume_type1_id = Fog::Mock.random_numbers(3).to_i
volume_type2_id = Fog::Mock.random_numbers(3).to_i
volume_type1_id = Fog::Mock.random_numbers(3).to_s
volume_type2_id = Fog::Mock.random_numbers(3).to_s
volume_type1 = {
"id" => volume_type1_id,

View file

@ -65,7 +65,7 @@ module Fog
true
end
def save
def save(options = {})
raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if persisted?
requires :flavor_id, :image_id
options = {

View file

@ -227,6 +227,7 @@ module Fog
options['Access-Control-Allow-Origin'] = access_control_allow_origin if access_control_allow_origin
options['Origin'] = origin if origin
options['Content-Disposition'] = content_disposition if content_disposition
options['Etag'] = etag if etag
options.merge!(metadata.to_headers)
data = service.put_object(directory.key, key, body, options)

View file

@ -31,7 +31,7 @@ module Fog
dc_root_folder = dc.vmFolder
# Filter the root path for this datacenter not to be used."
dc_root_folder_path=dc_root_folder.path.map { | id, name | name }.join("/")
paths = path.sub(/^\/?#{Regex.quote(dc_root_folder_path)}\/?/, '').split('/')
paths = path.sub(/^\/?#{Regexp.quote(dc_root_folder_path)}\/?/, '').split('/')
return dc_root_folder if paths.empty?
# Walk the tree resetting the folder pointer as we go

View file

@ -74,6 +74,8 @@ module Fog
request :destroy_network
request :create_vlan
request :destroy_vlan
request :snapshot_server
request :snapshot_revert
class Real

View file

@ -50,6 +50,7 @@ module Fog
attribute :hvm_boot_policy, :aliases => :HVM_boot_policy
attribute :hvm_boot_params, :aliases => :HVM_boot_params
attribute :pci_bus, :aliases => :PCI_bus
attribute :snapshots
def initialize(attributes={})
super
@ -198,14 +199,15 @@ module Fog
service.provision_server reference
end
# def snapshot
# requires :reference, :name_label
# data = service.snapshot_server(@reference, @name_label)
# merge_attributes(data.body)
# true
# end
def snapshot(name)
service.snapshot_server(reference, name)
end
def revert(snapshot_ref)
service.snapshot_revert(snapshot_ref)
end
end
end
end
end
end

View file

@ -0,0 +1,22 @@
module Fog
module Compute
class XenServer
class Real
def snapshot_revert( snapshot_ref, extra_args = {})
@connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'VM.revert'}, snapshot_ref)
end
end
class Mock
def snapshot_revert()
Fog::Mock.not_implemented
end
end
end
end
end

View file

@ -0,0 +1,22 @@
module Fog
module Compute
class XenServer
class Real
def snapshot_server( vm_ref , name, extra_args = {})
@connection.request({:parser => Fog::Parsers::XenServer::Base.new, :method => 'VM.snapshot'}, vm_ref, name)
end
end
class Mock
def snapshot_server()
Fog::Mock.not_implemented
end
end
end
end
end

21
lib/fog/xml.rb Normal file
View file

@ -0,0 +1,21 @@
require "nokogiri"
require "fog/core/parser"
module Fog
# @note Extracting XML components out of core is a work in progress.
#
# The {XML} module includes functionality that is common between APIs using
# XML to send and receive data.
#
# The intent is to provide common code for provider APIs using XML but not
# require it for those using JSON.
#
# @todo Add +require "fog/xml"+ and/or +include Fog::XML+ to providers using
# its services
#
module XML
end
end
require "fog/xml/sax_parser_connection"

View file

@ -0,0 +1,43 @@
module Fog
module XML
class SAXParserConnection < Fog::Core::Connection
# Makes a request using the connection using Excon
#
# @param [Hash] params
# @option params [String] :body text to be sent over a socket
# @option params [Hash<Symbol, String>] :headers The default headers to supply in a request
# @option params [String] :host The destination host's reachable DNS name or IP, in the form of a String
# @option params [String] :path appears after 'scheme://host:port/'
# @option params [Fixnum] :port The port on which to connect, to the destination host
# @option params [Hash] :query appended to the 'scheme://host:port/path/' in the form of '?key=value'
# @option params [String] :scheme The protocol; 'https' causes OpenSSL to be used
# @option params [Proc] :response_block
# @option params [Nokogiri::XML::SAX::Document] :parser
#
# @return [Excon::Response]
#
# @raise [Excon::Errors::StubNotFound]
# @raise [Excon::Errors::Timeout]
# @raise [Excon::Errors::SocketError]
#
def request(parser, params)
reset unless @persistent
# Prepare the SAX parser
data_stream = Nokogiri::XML::SAX::PushParser.new(parser)
params[:response_block] = lambda do |chunk, remaining, total|
data_stream << chunk
end
# Make request which read chunks into parser
response = @excon.request(params)
# Cease parsing and override response.body with parsed data
data_stream.finish
response.body = parser.response
response
end
end
end
end

View file

@ -0,0 +1,41 @@
Shindo.tests('Fog::Compute[:brightbox] | collaboration requests', ['brightbox']) do
tests('success') do
tests("#create_collaboration") do
pending if Fog.mocking?
collaboration = Fog::Compute[:brightbox].create_collaboration(:email => "paul@example.com", :role => "admin")
@collaboration_id = collaboration['id']
formats(Brightbox::Compute::Formats::Full::COLLABORATION, false) { collaboration }
end
tests("#list_collaborations") do
pending if Fog.mocking?
result = Fog::Compute[:brightbox].list_collaborations
formats(Brightbox::Compute::Formats::Collection::COLLABORATIONS, false) { result }
end
tests("#get_collaboration") do
pending if Fog.mocking?
result = Fog::Compute[:brightbox].get_collaboration(@collaboration_id)
formats(Brightbox::Compute::Formats::Full::COLLABORATION, false) { result }
end
tests("#destroy_collaboration") do
pending if Fog.mocking?
result = Fog::Compute[:brightbox].destroy_collaboration(@collaboration_id)
formats(Brightbox::Compute::Formats::Full::COLLABORATION, false) { result }
end
end
tests("failure") do
tests("get_collaboration('col-abcde')").raises(Excon::Errors::NotFound) do
pending if Fog.mocking?
Fog::Compute[:brightbox].get_collaboration("col-abcde")
end
end
end

View file

@ -8,6 +8,7 @@ module Fog
module LoadBalancer; end
module Server; end
module ServerGroup; end
module User; end
module Zone; end
end
end
@ -34,6 +35,9 @@ NilClass.send :include, Fog::Brightbox::Nullable::Server
Hash.send :include, Fog::Brightbox::Nullable::ServerGroup
NilClass.send :include, Fog::Brightbox::Nullable::ServerGroup
Hash.send :include, Fog::Brightbox::Nullable::User
NilClass.send :include, Fog::Brightbox::Nullable::User
Hash.send :include, Fog::Brightbox::Nullable::Zone
NilClass.send :include, Fog::Brightbox::Nullable::Zone
@ -75,8 +79,8 @@ class Brightbox
raise "No available images!" if images.empty?
images.select { |img| img["official"] && img["virtual_size"] != 0 }.sort_by { |img| img["disk_size"] }.first || images.first
end
end
module Formats
module Struct
CIP_PORT_TRANSLATOR = {
@ -231,6 +235,20 @@ class Brightbox
"email_address" => String
}
COLLABORATION = {
"id" => String,
"resource_type" => String,
"url" => String,
"status" => String,
"email" => Fog::Nullable::String,
"role" => String,
"role_label" => String,
"user" => Fog::Brightbox::Nullable::User,
"account" => Brightbox::Compute::Formats::Nested::ACCOUNT,
"inviter" => Brightbox::Compute::Formats::Nested::USER
}
ZONE = {
"id" => String,
"resource_type" => String,
@ -413,6 +431,19 @@ class Brightbox
"default_account" => NilClass
}
COLLABORATION = {
"id" => String,
"resource_type" => String,
"url" => String,
"status" => String,
"role" => String,
"role_label" => String,
"email" => Fog::Nullable::String,
"user" => Fog::Brightbox::Nullable::User,
"account" => Brightbox::Compute::Formats::Nested::ACCOUNT,
"inviter" => Brightbox::Compute::Formats::Nested::USER
}
ZONE = {
"id" => String,
"resource_type" => String,
@ -656,13 +687,25 @@ class Brightbox
"messaging_pref" => Fog::Boolean
}
COLLABORATION = {
"id" => String,
"resource_type" => String,
"url" => String,
"status" => String,
"role" => String,
"role_label" => String,
"email" => Fog::Nullable::String,
"user" => Fog::Brightbox::Nullable::User,
"account" => Brightbox::Compute::Formats::Nested::ACCOUNT,
"inviter" => Brightbox::Compute::Formats::Nested::USER
}
ZONE = {
"id" => String,
"resource_type" => String,
"url" => String,
"handle" => String
}
end
module Collection
@ -678,6 +721,7 @@ class Brightbox
SERVER_TYPES = [Brightbox::Compute::Formats::Collected::SERVER_TYPE]
USERS = [Brightbox::Compute::Formats::Collected::USER]
ZONES = [Brightbox::Compute::Formats::Collected::ZONE]
COLLABORATIONS = [Brightbox::Compute::Formats::Collected::COLLABORATION]
end
end

View file

@ -0,0 +1,67 @@
Shindo.tests('Fog::Compute[:brightbox] | user collaboration requests', ['brightbox']) do
@service = Fog::Compute[:brightbox]
tests("when accessing with user application") do
pending unless @service.authenticating_as_user?
tests("success") do
tests("#list_user_collaborations") do
pending if Fog.mocking?
result = @service.list_user_collaborations
formats(Brightbox::Compute::Formats::Collection::COLLABORATIONS, false) { result }
end
end
tests("failure") do
tests("get_user_collaboration('col-abcde')").raises(Excon::Errors::NotFound) do
pending if Fog.mocking?
@service.get_user_collaboration('col-abcde')
end
tests("accept_user_collaboration('col-abcde')").raises(Excon::Errors::NotFound) do
pending if Fog.mocking?
@service.accept_user_collaboration('col-abcde')
end
tests("reject_user_collaboration('col-abcde')").raises(Excon::Errors::NotFound) do
pending if Fog.mocking?
@service.reject_user_collaboration('col-abcde')
end
end
end
tests("when accessing with API client") do
pending if @service.authenticating_as_user?
tests("forbidden") do
tests("#list_user_collaborations").raises(Excon::Errors::Forbidden) do
pending if Fog.mocking?
result = @service.list_user_collaborations
formats(Brightbox::Compute::Formats::Collection::COLLABORATIONS, false) { result }
end
tests("get_user_collaboration('col-abcde')").raises(Excon::Errors::Forbidden) do
pending if Fog.mocking?
@service.get_user_collaboration('col-abcde')
end
tests("accept_user_collaboration('col-abcde')").raises(Excon::Errors::Forbidden) do
pending if Fog.mocking?
@service.accept_user_collaboration('col-abcde')
end
tests("reject_user_collaboration('col-abcde')").raises(Excon::Errors::Forbidden) do
pending if Fog.mocking?
@service.reject_user_collaboration('col-abcde')
end
end
end
end

View file

@ -0,0 +1,26 @@
Shindo.tests('Fog::Core::Connection', ['core']) do
raises(ArgumentError, "raises ArgumentError when no arguments given") do
Fog::Core::Connection.new
end
tests('new("http://example.com")') do
@instance = Fog::Core::Connection.new("http://example.com")
responds_to([:request, :reset])
tests('user agent').returns("fog/#{Fog::VERSION}") do
@instance.instance_variable_get(:@excon).data[:headers]['User-Agent']
end
end
tests('new("http://example.com", true)') do
Fog::Core::Connection.new("http://example.com", true)
end
tests('new("http://example.com", false, options")') do
options = {
:debug_response => false
}
Fog::Core::Connection.new("http://example.com", true, options)
end
end

View file

@ -1,6 +0,0 @@
Shindo.tests('Fog::Connection', 'core') do
tests('user_agent').returns("fog/#{Fog::VERSION}") do
conn = Fog::Connection.new("http://www.testserviceurl.com", false, {})
conn.instance_variable_get(:@excon).data[:headers]['User-Agent']
end
end

View file

@ -1,3 +1,16 @@
require 'simplecov'
require 'coveralls'
unless ENV['COVERAGE'] == 'false'
SimpleCov.command_name "shindo:#{Process.pid.to_s}"
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
SimpleCov::Formatter::HTMLFormatter,
Coveralls::SimpleCov::Formatter
]
SimpleCov.merge_timeout 3600
SimpleCov.start
end
ENV['FOG_RC'] = ENV['FOG_RC'] || File.expand_path('../.fog', __FILE__)
ENV['FOG_CREDENTIAL'] = ENV['FOG_CREDENTIAL'] || 'default'

View file

@ -18,6 +18,9 @@ def openvz_fog_test_server
# Server bootstrap took more than 120 secs!
end
end
openvz_fog_test_cleanup
server
end
@ -27,15 +30,18 @@ def openvz_fog_test_server_destroy
server.destroy if server
end
at_exit do
unless Fog.mocking?
server = openvz_service.servers.find { |s| s.name == '104' }
if server
server.wait_for(120) do
reload rescue nil; ready?
# Prepare a callback to destroy the long lived test server
def openvz_fog_test_cleanup
at_exit do
unless Fog.mocking?
server = openvz_service.servers.find { |s| s.name == '104' }
if server
server.wait_for(120) do
reload rescue nil; ready?
end
end
server.stop
openvz_fog_test_server_destroy
end
server.stop
openvz_fog_test_server_destroy
end
end

View file

@ -1,6 +1,11 @@
module Shindo
class Tests
unless Fog.mocking?
Fog.timeout = 2000
Fog::Logger.warning "Setting default fog timeout to #{Fog.timeout} seconds"
end
def given_a_load_balancer_service(&block)
@service = Fog::Rackspace::LoadBalancers.new
instance_eval(&block)
@ -23,6 +28,12 @@ module Shindo
end
end
def wait_for_request(description = "waiting", &block)
return if Fog.mocking?
tests(description) do
Fog.wait_for &block
end
end
def wait_for_server_deletion(server)
return if Fog.mocking?

View file

@ -4,7 +4,7 @@ Shindo.tests('Fog::Rackspace::BlockStorage | volume', ['rackspace']) do
options = { :display_name => "fog_#{Time.now.to_i.to_s}", :size => 100 }
model_tests(service.volumes, options, true) do
@instance.wait_for(timeout=1200) { ready? }
@instance.wait_for{ ready? }
tests('double save').raises(Fog::Rackspace::BlockStorage::IdentifierTaken) do
@instance.save
@ -18,7 +18,7 @@ Shindo.tests('Fog::Rackspace::BlockStorage | volume', ['rackspace']) do
tests('#snapshots').succeeds do
begin
snapshot = @instance.create_snapshot
snapshot.wait_for(timeout=1200) { ready? }
snapshot.wait_for { ready? }
returns(true) { @instance.snapshots.first.id == snapshot.id }
ensure

View file

@ -11,7 +11,7 @@ Shindo.tests('Fog::Compute::RackspaceV2 | metadata', ['rackspace']) do
:flavor_id => rackspace_test_flavor_id(service),
:image_id => rackspace_test_image_id(service))
@server.wait_for(timeout=1500) { ready? }
@server.wait_for { ready? }
tests('server') do
collection_tests(@server.metadata, {:key => 'my_key', :value => 'my_value'}) do
@ -21,7 +21,7 @@ Shindo.tests('Fog::Compute::RackspaceV2 | metadata', ['rackspace']) do
tests('image') do
@image = @server.create_image("fog_image_#{test_time}", :metadata => {:my_key => 'my_value'})
@image.wait_for(timeout = 1500) { ready? }
@image.wait_for { ready? }
tests("#all").succeeds do
pending if Fog.mocking? && !mocks_implemented
metadata = @image.metadata.all

View file

@ -66,7 +66,7 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
end
model_tests(service.servers, options, true) do
@instance.wait_for(timeout=1500) { ready? }
@instance.wait_for { ready? }
tests('#metadata[\'fog_test\']').returns('true') do
@instance.metadata['fog_test']
@ -79,12 +79,12 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
tests('#update').succeeds do
@instance.name = "fog_server_update"
@instance.access_ipv4_address= "10.10.0.1"
@instance.access_ipv6_address= "0:0:0:0:0:0:0:1"
@instance.access_ipv6_address= "::1"
@instance.save
sleep 60 unless Fog.mocking?
@instance.reload
returns("10.10.0.1") { @instance.access_ipv4_address }
returns("0:0:0:0:0:0:0:1") { @instance.access_ipv6_address }
returns("::1") { @instance.access_ipv6_address }
returns("fog_server_update") { @instance.name }
end
@ -93,13 +93,13 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
returns('REBOOT') { @instance.state }
end
@instance.wait_for(timeout=1500) { ready? }
@instance.wait_for { ready? }
tests('#reboot("HARD")').succeeds do
@instance.reboot('HARD')
returns('HARD_REBOOT') { @instance.state }
end
@instance.wait_for(timeout=1500) { ready? }
@instance.wait_for { ready? }
@test_image = nil
begin
tests('#create_image').succeeds do
@ -112,7 +112,7 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
end
sleep 30 unless Fog.mocking?
@instance.wait_for(timeout=1500) { ready? }
@instance.wait_for { ready? }
sleep 60 unless Fog.mocking?
tests('#rebuild').succeeds do
@instance.rebuild rackspace_test_image_id(service)
@ -120,7 +120,7 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
end
sleep 30 unless Fog.mocking?
@instance.wait_for(timeout=1500) { ready? }
@instance.wait_for { ready? }
sleep 60 unless Fog.mocking?
tests('#resize').succeeds do
@instance.resize(3)
@ -128,37 +128,37 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
end
sleep 30 unless Fog.mocking?
@instance.wait_for(timeout=1500) { ready?('VERIFY_RESIZE', ['ACTIVE', 'ERROR']) }
@instance.wait_for { ready?('VERIFY_RESIZE', ['ACTIVE', 'ERROR']) }
sleep 60 unless Fog.mocking?
tests('#confirm_resize').succeeds do
@instance.confirm_resize
end
sleep 30 unless Fog.mocking?
@instance.wait_for(timeout=1500) { ready? }
@instance.wait_for { ready? }
sleep 60 unless Fog.mocking?
tests('#resize').succeeds do
@instance.resize(2)
returns('RESIZE') { @instance.state }
end
@instance.wait_for(timeout=1500) { ready?('VERIFY_RESIZE') }
@instance.wait_for { ready?('VERIFY_RESIZE') }
sleep 60 unless Fog.mocking?
tests('#revert_resize').succeeds do
@instance.revert_resize
end
@instance.wait_for(timeout=1500) { ready? }
@instance.wait_for { ready? }
tests('#rescue').succeeds do
@instance.rescue
end
@instance.wait_for(timeout=1500) { ready?('RESCUE') }
@instance.wait_for { ready?('RESCUE') }
tests('#unrescue').succeeds do
@instance.unrescue
end
@instance.wait_for(timeout=1500) { ready? }
@instance.wait_for { ready? }
tests('#change_admin_password').succeeds do
@instance.change_admin_password('somerandompassword')
returns('PASSWORD') { @instance.state }
@ -168,28 +168,29 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
tests('attachments') do
begin
@volume = cbs_service.volumes.create(:size => 100, :display_name => "fog-#{Time.now.to_i.to_s}")
@volume.wait_for(timeout=1500) { ready? }
@volume.wait_for { ready? }
tests('#attach_volume').succeeds do
@instance.attach_volume(@volume)
end
tests('#attachments').returns(true) do
@instance.wait_for(timeout=1500) do
@instance.wait_for do
!attachments.empty?
end
@instance.attachments.any? {|a| a.volume_id == @volume.id }
end
ensure
@volume.wait_for(timeout=1500) { !attachments.empty? }
@volume.wait_for { !attachments.empty? }
@instance.attachments.each {|a| a.detach }
@volume.wait_for(timeout=1500) { ready? && attachments.empty? }
@volume.wait_for { ready? && attachments.empty? }
@volume.destroy if @volume
end
end
@instance.wait_for(timeout=1500) { ready? }
@instance.wait_for { ready? }
end
wait_for_server_deletion(@instance)
sleep 60 unless Fog.mocking?
tests("delete network #{@network.label}").succeeds do
@network.destroy if @network
@ -197,17 +198,17 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
#When after testing resize/resize_confirm we get a 409 when we try to resize_revert so I am going to split it into two blocks
model_tests(service.servers, options, true) do
@instance.wait_for(timeout=1500) { ready? }
@instance.wait_for { ready? }
tests('#resize').succeeds do
@instance.resize(4)
returns('RESIZE') { @instance.state }
end
@instance.wait_for(timeout=1500) { ready?('VERIFY_RESIZE') }
@instance.wait_for { ready?('VERIFY_RESIZE') }
sleep 60 unless Fog.mocking?
tests('#revert_resize').succeeds do
@instance.revert_resize
end
@instance.wait_for(timeout=1500) { ready? }
@instance.wait_for { ready? }
end
end

View file

@ -60,11 +60,6 @@ Shindo.tests('Fog::Rackspace::Storage | file', ['rackspace']) do
directories.
create(directory_attributes)
model_tests(@directory.files, file_attributes.merge(:etag => 'foo'), Fog.mocking?) do
tests('#save should not blow up with etag') do
@instance.save
end
end
model_tests(@directory.files, file_attributes, Fog.mocking?) do
@ -187,8 +182,29 @@ Shindo.tests('Fog::Rackspace::Storage | file', ['rackspace']) do
tests('#streaming_url').returns(0) do
@instance.streaming_url =~ /http:\/\/.+\.stream\..*#{@instance.key}/
end
end
end
tests('etags') do
text = lorem_file.read
md5 = Digest::MD5.new
md5 << text
etag = md5.hexdigest
begin
tests('valid tag').returns(true) do
@file = @directory.files.create :key => 'valid-etag.txt', :body => text, :etag => etag
@file.reload
@file.etag == etag
end
ensure
@file.destroy if @file
end
tests('invalid tag').raises(Fog::Storage::Rackspace::ServiceError) do
@directory.files.create :key => 'invalid-etag.txt', :body => text, :etag => "bad-bad-tag"
end
end
end
tests('#metadata keys') do

View file

@ -8,9 +8,8 @@ Shindo.tests('Fog::Rackspace::BlockStorage | snapshot_tests', ['rackspace']) do
'display_description' => Fog::Nullable::String,
'volume_id' => String,
'size' => Integer,
'created_at' => String,
'availability_zone' => String
}
'created_at' => String
}
get_snapshot_format = {
'snapshot' => snapshot_format

View file

@ -1,15 +1,8 @@
Shindo.tests('Fog::Rackspace::BlockStorage | volume_type_tests', ['rackspace']) do
volume_type_format = {
'name' => String,
'extra_specs' => Hash
}
list_volume_type_format = {
'volume_types' => [volume_type_format.merge({ 'id' => Integer })]
}
get_volume_type_format = {
'volume_type' => volume_type_format.merge({ 'id' => String })
'extra_specs' => Hash,
'id' => String
}
service = Fog::Rackspace::BlockStorage.new
@ -17,11 +10,11 @@ Shindo.tests('Fog::Rackspace::BlockStorage | volume_type_tests', ['rackspace'])
tests('success') do
volume_type_id = service.volume_types.first.id
tests("#list_volume_types").formats(list_volume_type_format) do
tests("#list_volume_types").formats('volume_types' => [volume_type_format]) do
service.list_volume_types.body
end
tests("#get_volume_type(#{volume_type_id})").formats(get_volume_type_format) do
tests("#get_volume_type(#{volume_type_id})").formats('volume_type' => volume_type_format) do
service.get_volume_type(volume_type_id).body
end
end

View file

@ -5,7 +5,7 @@ Shindo.tests('Fog::Compute::RackspaceV2 | address requests', ['rackspace']) do
tests('success') do
unless Fog.mocking?
@server = @service.servers.create(:flavor_id => 2, :image_id => "8a3a9f96-b997-46fd-b7a8-a9e740796ffd", :name => "address-tests-#{Time.now.to_i}")
@server.wait_for(timeout=1200) { ready? }
@server.wait_for { ready? }
@server_id = @server.id
else
@server_id = 42

View file

@ -1,9 +1,8 @@
Shindo.tests('Fog::Compute::RackspaceV2 | attachment_tests', ['rackspace']) do
compute_service = Fog::Compute::RackspaceV2.new
block_storage_service = Fog::Rackspace::BlockStorage.new
image_id = Fog.credentials[:rackspace_image_id] || compute_service.images.first.id
flavor_id = Fog.credentials[:rackspace_flavor_id] || compute_service.flavors.first.id
timeout = Fog.mocking? ? 1 : 10
image_id = rackspace_test_image_id(compute_service)
flavor_id = rackspace_test_flavor_id(compute_service)
attachment_format = {
'volumeAttachment' => {
@ -27,12 +26,12 @@ Shindo.tests('Fog::Compute::RackspaceV2 | attachment_tests', ['rackspace']) do
tests('success') do
until compute_service.get_server(server_id).body['server']['status'] == 'ACTIVE'
sleep timeout
wait_for_request("Waiting for server to become ready") do
compute_service.get_server(server_id).body['server']['status'] == 'ACTIVE'
end
until block_storage_service.get_volume(volume_id).body['volume']['status'] == 'available'
sleep timeout
wait_for_request("Waiting for Volume to be ready") do
block_storage_service.get_volume(volume_id).body['volume']['status'] == 'available'
end
tests("#attach_volume(#{server_id}, #{volume_id}, #{device_id})").formats(attachment_format) do
@ -43,8 +42,8 @@ Shindo.tests('Fog::Compute::RackspaceV2 | attachment_tests', ['rackspace']) do
compute_service.list_attachments(server_id).body
end
until block_storage_service.get_volume(volume_id).body['volume']['status'] == 'in-use'
sleep timeout
wait_for_request("Waiting for Volume to be ready") do
block_storage_service.get_volume(volume_id).body['volume']['status'] == 'in-use'
end
tests("#get_attachment(#{server_id}, #{volume_id})").formats(attachment_format) do

View file

@ -18,7 +18,7 @@ Shindo.tests('Fog::Compute::RackspaceV2 | flavor_tests', ['rackspace']) do
get_flavor_format = {
'flavor' => flavor_format.merge({
'OS-FLV-DISABLED:disabled' => Fog::Boolean,
'OS-FLV-EXT-DATA:ephemeral' => Integer,
'rxtx_factor' => Float,
'swap' => Integer
})

View file

@ -12,7 +12,7 @@ Shindo.tests('Fog::Compute::RackspaceV2 | metadata_tests', ['rackspace']) do
:flavor_id => 2,
:image_id => '3afe97b2-26dc-49c5-a2cc-a2fc8d80c001',
:metadata => metadata)
@server.wait_for(timeout = 1500) { ready? }
@server.wait_for { ready? }
@server_id = @server.id
@ -40,12 +40,12 @@ Shindo.tests('Fog::Compute::RackspaceV2 | metadata_tests', ['rackspace']) do
@service.set_metadata_item("servers", @server_id, "environment", "test").body
end
tests('delete_metadata_item').succeeds do
@service.delete_metadata_item("servers", @server_id, "environment").body
@service.delete_metadata_item("servers", @server_id, "environment")
end
end
tests("images") do
@image.wait_for(timeout = 1500) { ready? } unless Fog.mocking?
@image.wait_for { ready? } unless Fog.mocking?
tests('list_metadata').returns(metadata) do
h = @service.list_metadata("images", @image_id).body
@ -66,7 +66,7 @@ Shindo.tests('Fog::Compute::RackspaceV2 | metadata_tests', ['rackspace']) do
@service.set_metadata_item("images", @image_id, "environment", "test").body
end
tests('delete_metadata_item').succeeds do
@service.delete_metadata_item("images", @image_id, "environment").body
@service.delete_metadata_item("images", @image_id, "environment")
end
end
ensure

View file

@ -4,33 +4,36 @@ Shindo.tests('Fog::Rackspace::Database | database_tests', ['rackspace']) do
service = Fog::Rackspace::Databases.new
instance_name = 'fog' + Time.now.to_i.to_s
instance_id = service.create_instance(instance_name, 1, 1).body['instance']['id']
until service.get_instance(instance_id).body["instance"]["status"] == 'ACTIVE'
sleep 10
begin
@instance_id = service.create_instance(instance_name, 1, 1).body['instance']['id']
wait_for_request("waiting for database to be created") do
service.get_instance(@instance_id).body["instance"]["status"] == 'ACTIVE'
end
tests('success') do
database_name = 'fogdb' + Time.now.to_i.to_s
tests("#create_database(#{@instance_id}, #{database_name})").returns(202) do
service.create_database(@instance_id, database_name).status
end
tests("#list_databases{#{@instance_id})").formats(LIST_DATABASES_FORMAT) do
service.list_databases(@instance_id).body
end
tests("#delete_database(#{@instance_id}, #{database_name})").returns(202) do
service.delete_database(@instance_id, database_name).status
end
end
tests('failure') do
tests("#create_database(#{@instance_id}, '') => Invalid Create Critera").raises(Fog::Rackspace::Databases::BadRequest) do
service.create_database(@instance_id, '')
end
end
ensure
service.delete_instance(@instance_id) if @instance_id
end
tests('success') do
database_name = 'fogdb' + Time.now.to_i.to_s
tests("#create_database(#{instance_id}, #{database_name})").succeeds do
service.create_database(instance_id, database_name).body
end
tests("#list_databases{#{instance_id})").formats(LIST_DATABASES_FORMAT) do
service.list_databases(instance_id).body
end
tests("#delete_database(#{instance_id}, #{database_name})").succeeds do
service.delete_database(instance_id, database_name)
end
end
tests('failure') do
tests("#create_database(#{instance_id}, '') => Invalid Create Critera").raises(Fog::Rackspace::Databases::BadRequest) do
service.create_database(instance_id, '')
end
end
service.delete_instance(instance_id)
end

View file

@ -6,24 +6,24 @@ Shindo.tests('Fog::Rackspace::Database | user_tests', ['rackspace']) do
instance_name = 'fog' + Time.now.to_i.to_s
instance_id = service.create_instance(instance_name, 1, 1).body['instance']['id']
until service.get_instance(instance_id).body["instance"]["status"] == 'ACTIVE'
sleep 10
wait_for_request("Waiting for database to be created") do
service.get_instance(instance_id).body["instance"]["status"] == 'ACTIVE'
end
tests('success') do
user_name = 'fog' + Time.now.to_i.to_s
password = 'password1'
tests("#create_user(#{instance_id}, #{user_name}, #{password})").succeeds do
service.create_user(instance_id, user_name, password).body
tests("#create_user(#{instance_id}, #{user_name}, #{password})").returns(202) do
service.create_user(instance_id, user_name, password).status
end
tests("#list_users{#{instance_id})").formats(LIST_USERS_FORMAT) do
service.list_users(instance_id).body
end
tests("#delete_user(#{instance_id}, #{user_name})").succeeds do
service.delete_user(instance_id, user_name)
tests("#delete_user(#{instance_id}, #{user_name})").returns(202) do
service.delete_user(instance_id, user_name).status
end
end

View file

@ -6,11 +6,13 @@ Shindo.tests('Fog::Rackspace::LoadBalancers | usage', ['rackspace']) do
tests('success') do
tests("#get_usage()").formats(USAGE_FORMAT) do
@service.get_usage.body
pending
# @service.get_usage.body
end
tests("#get_usage(:start_time => '2010-05-10', :end_time => '2010-05-11')").formats(USAGE_FORMAT) do
@service.get_usage(:start_time => '2010-05-10', :end_time => '2010-05-11').body
pending
# @service.get_usage(:start_time => '2010-05-10', :end_time => '2010-05-11').body
end
end
end

View file

@ -60,7 +60,8 @@ Shindo.tests('Fog::Compute[:xenserver] | server model', ['xenserver']) do
:pv_kernel,
:pv_ramdisk,
:pv_legacy_args,
:pv_bootloader_args
:pv_bootloader_args,
:snapshots
]
tests("The server model should respond to") do
attributes.each do |attribute|
@ -157,6 +158,15 @@ Shindo.tests('Fog::Compute[:xenserver] | server model', ['xenserver']) do
server.pv_bootloader == 'supergrub'
end
tests("Creating a snapshot") do
snap_ref = server.snapshot('newsnapshot')
tests("it should create a snapshot") do
snap_ref = server.snapshot('newsnapshot')
servers.get(snap_ref).reference == snap_ref
end
test("and destroy it afterwards") { servers.get(snap_ref).destroy }
end
test("be able to be destroyed!") do
server.destroy
servers.get_by_name('fog-test-server-shindo') == nil