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 - 1.9.3
- 2.0.0 - 2.0.0
script: FOG_MOCK=true bundle exec shindont script: bundle exec rake travis
notifications: notifications:
email: false email: false

View file

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

View file

@ -47,20 +47,21 @@ end
GEM_NAME = "#{name}" GEM_NAME = "#{name}"
task :default => :test task :default => :test
task :travis => ['test:travis', 'coveralls_push_workaround']
require "tasks/test_task" require "tasks/test_task"
Fog::Rake::TestTask.new Fog::Rake::TestTask.new
namespace :test do namespace :test do
mock = 'true' || ENV['FOG_MOCK']
task :travis do
sh("export FOG_MOCK=#{mock} && bundle exec shindont")
end
task :vsphere do task :vsphere do
[true].each do |mock|
sh("export FOG_MOCK=#{mock} && bundle exec shindont tests/vsphere") sh("export FOG_MOCK=#{mock} && bundle exec shindont tests/vsphere")
end
end end
task :openvz do task :openvz do
[true].each do |mock|
sh("export FOG_MOCK=#{mock} && bundle exec shindont tests/openvz") sh("export FOG_MOCK=#{mock} && bundle exec shindont tests/openvz")
end
end end
end end
@ -186,3 +187,12 @@ end
require "tasks/changelog_task" require "tasks/changelog_task"
Fog::Rake::ChangelogTask.new 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 #!/usr/bin/env ruby
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'fog')) require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'fog'))
require 'optparse'
require 'irb' require 'irb'
require 'yaml' 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.credential = ARGV.first ? ARGV.first.to_sym : nil
Fog.mock! if ENV['FOG_MOCK'] Fog.mock! if ENV['FOG_MOCK']
if Fog.credentials.empty? if Fog.credentials.empty?

View file

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

View file

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

View file

@ -1,14 +1,12 @@
require 'fog/brightbox' require 'fog/brightbox'
require 'fog/compute' require 'fog/compute'
require 'fog/brightbox/oauth2' require 'fog/brightbox/compute/shared'
require 'fog/brightbox/compute/image_selector' require 'fog/brightbox/compute/image_selector'
module Fog module Fog
module Compute module Compute
class Brightbox < Fog::Service class Brightbox < Fog::Service
API_URL = "https://api.gb1.brightbox.com/"
# Client credentials # Client credentials
requires :brightbox_client_id, :brightbox_secret requires :brightbox_client_id, :brightbox_secret
@ -34,6 +32,8 @@ module Fog
model :application model :application
collection :api_clients collection :api_clients
model :api_client model :api_client
collection :collaborations
model :collaboration
collection :servers collection :servers
model :server model :server
collection :server_groups collection :server_groups
@ -54,17 +54,22 @@ module Fog
model :cloud_ip model :cloud_ip
collection :users collection :users
model :user model :user
collection :user_collaborations
model :user_collaboration
request_path 'fog/brightbox/requests/compute' request_path 'fog/brightbox/requests/compute'
request :accept_user_collaboration
request :activate_console_server request :activate_console_server
request :add_listeners_load_balancer request :add_listeners_load_balancer
request :add_nodes_load_balancer request :add_nodes_load_balancer
request :add_servers_server_group request :add_servers_server_group
request :apply_to_firewall_policy request :apply_to_firewall_policy
request :accept_user_collaboration
request :remove_firewall_policy request :remove_firewall_policy
request :create_api_client request :create_api_client
request :create_application request :create_application
request :create_cloud_ip request :create_cloud_ip
request :create_collaboration
request :create_firewall_policy request :create_firewall_policy
request :create_firewall_rule request :create_firewall_rule
request :create_image request :create_image
@ -74,17 +79,20 @@ module Fog
request :delete_api_client request :delete_api_client
request :delete_application request :delete_application
request :delete_cloud_ip request :delete_cloud_ip
request :delete_collaboration
request :delete_firewall_policy request :delete_firewall_policy
request :delete_firewall_rule request :delete_firewall_rule
request :delete_image request :delete_image
request :delete_load_balancer request :delete_load_balancer
request :delete_server request :delete_server
request :delete_server_group request :delete_server_group
request :delete_user_collaboration
request :get_account request :get_account
request :get_api_client request :get_api_client
request :get_application request :get_application
request :get_authenticated_user request :get_authenticated_user
request :get_cloud_ip request :get_cloud_ip
request :get_collaboration
request :get_firewall_policy request :get_firewall_policy
request :get_firewall_rule request :get_firewall_rule
request :get_image request :get_image
@ -95,11 +103,13 @@ module Fog
request :get_server_group request :get_server_group
request :get_server_type request :get_server_type
request :get_user request :get_user
request :get_user_collaboration
request :get_zone request :get_zone
request :list_accounts request :list_accounts
request :list_api_clients request :list_api_clients
request :list_applications request :list_applications
request :list_cloud_ips request :list_cloud_ips
request :list_collaborations
request :list_firewall_policies request :list_firewall_policies
request :list_images request :list_images
request :list_load_balancers request :list_load_balancers
@ -107,16 +117,21 @@ module Fog
request :list_server_types request :list_server_types
request :list_servers request :list_servers
request :list_users request :list_users
request :list_user_collaborations
request :list_zones request :list_zones
request :map_cloud_ip request :map_cloud_ip
request :move_servers_server_group request :move_servers_server_group
request :reject_user_collaboration
request :remove_listeners_load_balancer request :remove_listeners_load_balancer
request :remove_nodes_load_balancer request :remove_nodes_load_balancer
request :remove_servers_server_group request :remove_servers_server_group
request :resend_collaboration
request :reset_ftp_password_account request :reset_ftp_password_account
request :reset_ftp_password_scoped_account request :reset_ftp_password_scoped_account
request :reset_secret_api_client request :reset_secret_api_client
request :reset_secret_application request :reset_secret_application
request :resend_collaboration
request :reject_user_collaboration
request :shutdown_server request :shutdown_server
request :snapshot_server request :snapshot_server
request :start_server request :start_server
@ -135,220 +150,13 @@ module Fog
request :update_server_group request :update_server_group
request :update_user 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 # The Mock Service allows you to run a fake instance of the Service
# which makes no real connections. # which makes no real connections.
# #
# @todo Implement # @todo Implement
# #
class Mock class Mock
include Shared include Fog::Brightbox::Compute::Shared
def request(method, path, expected_responses, parameters = {}) def request(method, path, expected_responses, parameters = {})
_request _request
@ -373,7 +181,7 @@ module Fog
# service. # service.
# #
class Real class Real
include Shared include Fog::Brightbox::Compute::Shared
# Makes an API request to the given path using passed options or those # Makes an API request to the given path using passed options or those
# set with the service setup # 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/deprecation'
require 'fog/core/errors' require 'fog/core/errors'
require 'fog/core/hmac' require 'fog/core/hmac'
require 'fog/core/json'
require 'fog/core/logger' require 'fog/core/logger'
require 'fog/core/model' require 'fog/core/model'
require 'fog/core/mock' 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/provider'
require 'fog/core/service' require 'fog/core/service'
require 'fog/core/ssh' require 'fog/core/ssh'
@ -32,6 +30,13 @@ require 'fog/core/time'
require 'fog/core/timeout' require 'fog/core/timeout'
require 'fog/core/wait_for' 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 # service wrappers
require 'fog/compute' require 'fog/compute'
require 'fog/identity' require 'fog/identity'

View file

@ -1,40 +1,73 @@
module Fog module Fog
class Connection module Core
def initialize(url, persistent=false, params={}) # Fog::Core::Connection is a generic class to contain a HTTP link to an API.
unless params.has_key?(:debug_response) #
params[:debug_response] = true # It is intended to be subclassed by providers who can then add their own
end # modifications such as authentication or response object.
params[:headers] ||= {} #
params[:headers]['User-Agent'] ||= "fog/#{Fog::VERSION}" class Connection
@excon = Excon.new(url, params) # Prepares the connection and sets defaults for any future requests.
@persistent = persistent #
end # @param [String] url The destination URL
# @param persistent [Boolean]
def request(params, &block) # @param [Hash] params
unless @persistent # @option params [String] :body Default text to be sent over a socket. Only used if :body absent in Connection#request params
reset # @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
end # @option params [String] :host The destination host's reachable DNS name or IP, in the form of a String
unless block_given? # @option params [String] :path Default path; appears after 'scheme://host:port/'. Only used if params[:path] is not supplied to Connection#request
if (parser = params.delete(:parser)) # @option params [Fixnum] :port The port on which to connect, to the destination host
body = Nokogiri::XML::SAX::PushParser.new(parser) # @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
params[:response_block] = lambda { |chunk, remaining, total| body << chunk } # @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 end
params[:headers] ||= {}
params[:headers]['User-Agent'] ||= "fog/#{Fog::VERSION}"
@excon = Excon.new(url, params)
@persistent = persistent
end end
response = @excon.request(params, &block) # Makes a request using the connection using Excon
#
if parser # @param [Hash] params
body.finish # @option params [String] :body text to be sent over a socket
response.body = parser.response # @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 end
response # Make {#request} available even when it has been overidden by a subclass
end # to allow backwards compatibility.
#
alias_method :original_request, :request
protected :original_request
def reset # Closes the connection
@excon.reset #
def reset
@excon.reset
end
end end
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 Fog
module Parsers module Parsers

View file

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

View file

@ -12,6 +12,37 @@ module Fog
attribute :creation_timestamp, :aliases => 'creationTimestamp' attribute :creation_timestamp, :aliases => 'creationTimestamp'
attribute :description attribute :description
attribute :preferred_kernel, :aliases => 'preferredKernel' 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
end end

View file

@ -16,8 +16,8 @@ module Fog
attribute :metadata attribute :metadata
def destroy def destroy
requires :name requires :name, :zone
service.delete_server(name) service.delete_server(name, zone)
end end
def image def image
@ -25,22 +25,37 @@ module Fog
end end
def public_ip_address def public_ip_address
if self.network_interfaces.count ip = nil
self.network_interfaces[0]["networkIP"] if self.network_interfaces
else self.network_interfaces.each do |netif|
nil netif["accessConfigs"].each do |access_config|
if access_config["name"] == "External NAT"
ip = access_config['natIP']
end
end
end
end end
ip
end end
def ready? def ready?
data = service.get_server(self.name, self.zone_name).body self.state == RUNNING
data['zone_name'] = self.zone_name
self.merge_attributes(data)
self.state == RUNNING_STATE
end end
def zone 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 end
def save def save
@ -49,29 +64,20 @@ module Fog
requires :machine_type requires :machine_type
requires :zone_name 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( data = service.insert_server(
name, name,
image_name, image_name,
zone_name, zone_name,
machine_type) machine_type,
metadata)
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
end end
end end

View file

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

View file

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

View file

@ -13,10 +13,16 @@ module Fog
class Real class Real
def get_server(server_name, zone_name) 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 api_method = @compute.instances.get
parameters = { parameters = {
'project' => @project, 'project' => @project,
'zone' => zone_name, 'zone' => zone,
'instance' => server_name 'instance' => server_name
} }

View file

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

View file

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

View file

@ -10,8 +10,11 @@ module Fog
recognizes :persistent, :connection_options recognizes :persistent, :connection_options
recognizes :hp_use_upass_auth_style, :hp_auth_version, :user_agent 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 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_path 'fog/hp/models/storage'
model :directory model :directory
@ -167,14 +170,26 @@ module Fog
encoded_path = "#{path}/#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}" encoded_path = "#{path}/#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}"
string_to_sign = "#{method}\n#{expires}\n#{sig_path}" 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) signature = nil
# Compatible with 1.8.7 onwards
hmac = OpenSSL::HMAC.new(@hp_secret_key, OpenSSL::Digest::SHA1.new) # HP uses a different strategy to create the signature that is passed to swift than OpenStack.
signed_string = hmac.update(string_to_sign).hexdigest # 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.
signature = @hp_tenant_id.to_s + ":" + @hp_access_key.to_s + ":" + signed_string if @os_account_meta_temp_url_key then
signature = Fog::HP.escape(signature) 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 # generate the temp url using the signature and expiry
"#{scheme}://#{host}:#{port}#{encoded_path}?temp_url_sig=#{signature}&temp_url_expires=#{expires}" "#{scheme}://#{host}:#{port}#{encoded_path}?temp_url_sig=#{signature}&temp_url_expires=#{expires}"
@ -217,6 +232,7 @@ module Fog
end end
@hp_secret_key = options[:hp_secret_key] @hp_secret_key = options[:hp_secret_key]
@hp_tenant_id = options[:hp_tenant_id] @hp_tenant_id = options[:hp_tenant_id]
@os_account_meta_temp_url_key = options[:os_account_meta_temp_url_key]
end end
def data def data
@ -254,6 +270,7 @@ module Fog
options[:hp_service_type] = "Object Storage" options[:hp_service_type] = "Object Storage"
@hp_tenant_id = options[:hp_tenant_id] @hp_tenant_id = options[:hp_tenant_id]
@hp_avl_zone = options[:hp_avl_zone] @hp_avl_zone = options[:hp_avl_zone]
@os_account_meta_temp_url_key = options[:os_account_meta_temp_url_key]
### Make the authentication call ### Make the authentication call
if (auth_version == :v2) if (auth_version == :v2)

View file

@ -1,6 +1,18 @@
require 'multi_json' require "multi_json"
module Fog 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 module JSON
def self.sanitize(data) def self.sanitize(data)
@ -28,6 +40,5 @@ module Fog
def self.decode(obj) def self.decode(obj)
MultiJson.decode(obj) MultiJson.decode(obj)
end end
end end
end end

View file

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

View file

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

View file

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

View file

@ -31,7 +31,7 @@ module Fog
dc_root_folder = dc.vmFolder dc_root_folder = dc.vmFolder
# Filter the root path for this datacenter not to be used." # Filter the root path for this datacenter not to be used."
dc_root_folder_path=dc_root_folder.path.map { | id, name | name }.join("/") 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? return dc_root_folder if paths.empty?
# Walk the tree resetting the folder pointer as we go # Walk the tree resetting the folder pointer as we go

View file

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

View file

@ -50,6 +50,7 @@ module Fog
attribute :hvm_boot_policy, :aliases => :HVM_boot_policy attribute :hvm_boot_policy, :aliases => :HVM_boot_policy
attribute :hvm_boot_params, :aliases => :HVM_boot_params attribute :hvm_boot_params, :aliases => :HVM_boot_params
attribute :pci_bus, :aliases => :PCI_bus attribute :pci_bus, :aliases => :PCI_bus
attribute :snapshots
def initialize(attributes={}) def initialize(attributes={})
super super
@ -198,14 +199,15 @@ module Fog
service.provision_server reference service.provision_server reference
end end
# def snapshot def snapshot(name)
# requires :reference, :name_label service.snapshot_server(reference, name)
# data = service.snapshot_server(@reference, @name_label) end
# merge_attributes(data.body)
# true def revert(snapshot_ref)
# end service.snapshot_revert(snapshot_ref)
end
end end
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 LoadBalancer; end
module Server; end module Server; end
module ServerGroup; end module ServerGroup; end
module User; end
module Zone; end module Zone; end
end end
end end
@ -34,6 +35,9 @@ NilClass.send :include, Fog::Brightbox::Nullable::Server
Hash.send :include, Fog::Brightbox::Nullable::ServerGroup Hash.send :include, Fog::Brightbox::Nullable::ServerGroup
NilClass.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 Hash.send :include, Fog::Brightbox::Nullable::Zone
NilClass.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? 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 images.select { |img| img["official"] && img["virtual_size"] != 0 }.sort_by { |img| img["disk_size"] }.first || images.first
end end
end end
module Formats module Formats
module Struct module Struct
CIP_PORT_TRANSLATOR = { CIP_PORT_TRANSLATOR = {
@ -231,6 +235,20 @@ class Brightbox
"email_address" => String "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 = { ZONE = {
"id" => String, "id" => String,
"resource_type" => String, "resource_type" => String,
@ -413,6 +431,19 @@ class Brightbox
"default_account" => NilClass "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 = { ZONE = {
"id" => String, "id" => String,
"resource_type" => String, "resource_type" => String,
@ -656,13 +687,25 @@ class Brightbox
"messaging_pref" => Fog::Boolean "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 = { ZONE = {
"id" => String, "id" => String,
"resource_type" => String, "resource_type" => String,
"url" => String, "url" => String,
"handle" => String "handle" => String
} }
end end
module Collection module Collection
@ -678,6 +721,7 @@ class Brightbox
SERVER_TYPES = [Brightbox::Compute::Formats::Collected::SERVER_TYPE] SERVER_TYPES = [Brightbox::Compute::Formats::Collected::SERVER_TYPE]
USERS = [Brightbox::Compute::Formats::Collected::USER] USERS = [Brightbox::Compute::Formats::Collected::USER]
ZONES = [Brightbox::Compute::Formats::Collected::ZONE] ZONES = [Brightbox::Compute::Formats::Collected::ZONE]
COLLABORATIONS = [Brightbox::Compute::Formats::Collected::COLLABORATION]
end end
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_RC'] = ENV['FOG_RC'] || File.expand_path('../.fog', __FILE__)
ENV['FOG_CREDENTIAL'] = ENV['FOG_CREDENTIAL'] || 'default' 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! # Server bootstrap took more than 120 secs!
end end
end end
openvz_fog_test_cleanup
server server
end end
@ -27,15 +30,18 @@ def openvz_fog_test_server_destroy
server.destroy if server server.destroy if server
end end
at_exit do # Prepare a callback to destroy the long lived test server
unless Fog.mocking? def openvz_fog_test_cleanup
server = openvz_service.servers.find { |s| s.name == '104' } at_exit do
if server unless Fog.mocking?
server.wait_for(120) do server = openvz_service.servers.find { |s| s.name == '104' }
reload rescue nil; ready? if server
server.wait_for(120) do
reload rescue nil; ready?
end
end end
server.stop
openvz_fog_test_server_destroy
end end
server.stop
openvz_fog_test_server_destroy
end end
end end

View file

@ -1,6 +1,11 @@
module Shindo module Shindo
class Tests 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) def given_a_load_balancer_service(&block)
@service = Fog::Rackspace::LoadBalancers.new @service = Fog::Rackspace::LoadBalancers.new
instance_eval(&block) instance_eval(&block)
@ -23,6 +28,12 @@ module Shindo
end end
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) def wait_for_server_deletion(server)
return if Fog.mocking? 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 } options = { :display_name => "fog_#{Time.now.to_i.to_s}", :size => 100 }
model_tests(service.volumes, options, true) do 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 tests('double save').raises(Fog::Rackspace::BlockStorage::IdentifierTaken) do
@instance.save @instance.save
@ -18,7 +18,7 @@ Shindo.tests('Fog::Rackspace::BlockStorage | volume', ['rackspace']) do
tests('#snapshots').succeeds do tests('#snapshots').succeeds do
begin begin
snapshot = @instance.create_snapshot snapshot = @instance.create_snapshot
snapshot.wait_for(timeout=1200) { ready? } snapshot.wait_for { ready? }
returns(true) { @instance.snapshots.first.id == snapshot.id } returns(true) { @instance.snapshots.first.id == snapshot.id }
ensure ensure

View file

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

View file

@ -66,7 +66,7 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
end end
model_tests(service.servers, options, true) do model_tests(service.servers, options, true) do
@instance.wait_for(timeout=1500) { ready? } @instance.wait_for { ready? }
tests('#metadata[\'fog_test\']').returns('true') do tests('#metadata[\'fog_test\']').returns('true') do
@instance.metadata['fog_test'] @instance.metadata['fog_test']
@ -79,12 +79,12 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
tests('#update').succeeds do tests('#update').succeeds do
@instance.name = "fog_server_update" @instance.name = "fog_server_update"
@instance.access_ipv4_address= "10.10.0.1" @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 @instance.save
sleep 60 unless Fog.mocking? sleep 60 unless Fog.mocking?
@instance.reload @instance.reload
returns("10.10.0.1") { @instance.access_ipv4_address } 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 } returns("fog_server_update") { @instance.name }
end end
@ -93,13 +93,13 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
returns('REBOOT') { @instance.state } returns('REBOOT') { @instance.state }
end end
@instance.wait_for(timeout=1500) { ready? } @instance.wait_for { ready? }
tests('#reboot("HARD")').succeeds do tests('#reboot("HARD")').succeeds do
@instance.reboot('HARD') @instance.reboot('HARD')
returns('HARD_REBOOT') { @instance.state } returns('HARD_REBOOT') { @instance.state }
end end
@instance.wait_for(timeout=1500) { ready? } @instance.wait_for { ready? }
@test_image = nil @test_image = nil
begin begin
tests('#create_image').succeeds do tests('#create_image').succeeds do
@ -112,7 +112,7 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
end end
sleep 30 unless Fog.mocking? sleep 30 unless Fog.mocking?
@instance.wait_for(timeout=1500) { ready? } @instance.wait_for { ready? }
sleep 60 unless Fog.mocking? sleep 60 unless Fog.mocking?
tests('#rebuild').succeeds do tests('#rebuild').succeeds do
@instance.rebuild rackspace_test_image_id(service) @instance.rebuild rackspace_test_image_id(service)
@ -120,7 +120,7 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
end end
sleep 30 unless Fog.mocking? sleep 30 unless Fog.mocking?
@instance.wait_for(timeout=1500) { ready? } @instance.wait_for { ready? }
sleep 60 unless Fog.mocking? sleep 60 unless Fog.mocking?
tests('#resize').succeeds do tests('#resize').succeeds do
@instance.resize(3) @instance.resize(3)
@ -128,37 +128,37 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
end end
sleep 30 unless Fog.mocking? 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? sleep 60 unless Fog.mocking?
tests('#confirm_resize').succeeds do tests('#confirm_resize').succeeds do
@instance.confirm_resize @instance.confirm_resize
end end
sleep 30 unless Fog.mocking? sleep 30 unless Fog.mocking?
@instance.wait_for(timeout=1500) { ready? } @instance.wait_for { ready? }
sleep 60 unless Fog.mocking? sleep 60 unless Fog.mocking?
tests('#resize').succeeds do tests('#resize').succeeds do
@instance.resize(2) @instance.resize(2)
returns('RESIZE') { @instance.state } returns('RESIZE') { @instance.state }
end end
@instance.wait_for(timeout=1500) { ready?('VERIFY_RESIZE') } @instance.wait_for { ready?('VERIFY_RESIZE') }
sleep 60 unless Fog.mocking? sleep 60 unless Fog.mocking?
tests('#revert_resize').succeeds do tests('#revert_resize').succeeds do
@instance.revert_resize @instance.revert_resize
end end
@instance.wait_for(timeout=1500) { ready? } @instance.wait_for { ready? }
tests('#rescue').succeeds do tests('#rescue').succeeds do
@instance.rescue @instance.rescue
end end
@instance.wait_for(timeout=1500) { ready?('RESCUE') } @instance.wait_for { ready?('RESCUE') }
tests('#unrescue').succeeds do tests('#unrescue').succeeds do
@instance.unrescue @instance.unrescue
end end
@instance.wait_for(timeout=1500) { ready? } @instance.wait_for { ready? }
tests('#change_admin_password').succeeds do tests('#change_admin_password').succeeds do
@instance.change_admin_password('somerandompassword') @instance.change_admin_password('somerandompassword')
returns('PASSWORD') { @instance.state } returns('PASSWORD') { @instance.state }
@ -168,28 +168,29 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server', ['rackspace']) do
tests('attachments') do tests('attachments') do
begin begin
@volume = cbs_service.volumes.create(:size => 100, :display_name => "fog-#{Time.now.to_i.to_s}") @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 tests('#attach_volume').succeeds do
@instance.attach_volume(@volume) @instance.attach_volume(@volume)
end end
tests('#attachments').returns(true) do tests('#attachments').returns(true) do
@instance.wait_for(timeout=1500) do @instance.wait_for do
!attachments.empty? !attachments.empty?
end end
@instance.attachments.any? {|a| a.volume_id == @volume.id } @instance.attachments.any? {|a| a.volume_id == @volume.id }
end end
ensure ensure
@volume.wait_for(timeout=1500) { !attachments.empty? } @volume.wait_for { !attachments.empty? }
@instance.attachments.each {|a| a.detach } @instance.attachments.each {|a| a.detach }
@volume.wait_for(timeout=1500) { ready? && attachments.empty? } @volume.wait_for { ready? && attachments.empty? }
@volume.destroy if @volume @volume.destroy if @volume
end end
end end
@instance.wait_for(timeout=1500) { ready? } @instance.wait_for { ready? }
end end
wait_for_server_deletion(@instance) wait_for_server_deletion(@instance)
sleep 60 unless Fog.mocking?
tests("delete network #{@network.label}").succeeds do tests("delete network #{@network.label}").succeeds do
@network.destroy if @network @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 #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 model_tests(service.servers, options, true) do
@instance.wait_for(timeout=1500) { ready? } @instance.wait_for { ready? }
tests('#resize').succeeds do tests('#resize').succeeds do
@instance.resize(4) @instance.resize(4)
returns('RESIZE') { @instance.state } returns('RESIZE') { @instance.state }
end end
@instance.wait_for(timeout=1500) { ready?('VERIFY_RESIZE') } @instance.wait_for { ready?('VERIFY_RESIZE') }
sleep 60 unless Fog.mocking? sleep 60 unless Fog.mocking?
tests('#revert_resize').succeeds do tests('#revert_resize').succeeds do
@instance.revert_resize @instance.revert_resize
end end
@instance.wait_for(timeout=1500) { ready? } @instance.wait_for { ready? }
end end
end end

View file

@ -60,11 +60,6 @@ Shindo.tests('Fog::Rackspace::Storage | file', ['rackspace']) do
directories. directories.
create(directory_attributes) 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 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 tests('#streaming_url').returns(0) do
@instance.streaming_url =~ /http:\/\/.+\.stream\..*#{@instance.key}/ @instance.streaming_url =~ /http:\/\/.+\.stream\..*#{@instance.key}/
end 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
end
tests('#metadata keys') do tests('#metadata keys') do

View file

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

View file

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

View file

@ -5,7 +5,7 @@ Shindo.tests('Fog::Compute::RackspaceV2 | address requests', ['rackspace']) do
tests('success') do tests('success') do
unless Fog.mocking? 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 = @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 @server_id = @server.id
else else
@server_id = 42 @server_id = 42

View file

@ -1,9 +1,8 @@
Shindo.tests('Fog::Compute::RackspaceV2 | attachment_tests', ['rackspace']) do Shindo.tests('Fog::Compute::RackspaceV2 | attachment_tests', ['rackspace']) do
compute_service = Fog::Compute::RackspaceV2.new compute_service = Fog::Compute::RackspaceV2.new
block_storage_service = Fog::Rackspace::BlockStorage.new block_storage_service = Fog::Rackspace::BlockStorage.new
image_id = Fog.credentials[:rackspace_image_id] || compute_service.images.first.id image_id = rackspace_test_image_id(compute_service)
flavor_id = Fog.credentials[:rackspace_flavor_id] || compute_service.flavors.first.id flavor_id = rackspace_test_flavor_id(compute_service)
timeout = Fog.mocking? ? 1 : 10
attachment_format = { attachment_format = {
'volumeAttachment' => { 'volumeAttachment' => {
@ -27,12 +26,12 @@ Shindo.tests('Fog::Compute::RackspaceV2 | attachment_tests', ['rackspace']) do
tests('success') do tests('success') do
until compute_service.get_server(server_id).body['server']['status'] == 'ACTIVE' wait_for_request("Waiting for server to become ready") do
sleep timeout compute_service.get_server(server_id).body['server']['status'] == 'ACTIVE'
end end
until block_storage_service.get_volume(volume_id).body['volume']['status'] == 'available' wait_for_request("Waiting for Volume to be ready") do
sleep timeout block_storage_service.get_volume(volume_id).body['volume']['status'] == 'available'
end end
tests("#attach_volume(#{server_id}, #{volume_id}, #{device_id})").formats(attachment_format) do 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 compute_service.list_attachments(server_id).body
end end
until block_storage_service.get_volume(volume_id).body['volume']['status'] == 'in-use' wait_for_request("Waiting for Volume to be ready") do
sleep timeout block_storage_service.get_volume(volume_id).body['volume']['status'] == 'in-use'
end end
tests("#get_attachment(#{server_id}, #{volume_id})").formats(attachment_format) do 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 = { get_flavor_format = {
'flavor' => flavor_format.merge({ 'flavor' => flavor_format.merge({
'OS-FLV-DISABLED:disabled' => Fog::Boolean, 'OS-FLV-EXT-DATA:ephemeral' => Integer,
'rxtx_factor' => Float, 'rxtx_factor' => Float,
'swap' => Integer 'swap' => Integer
}) })

View file

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

View file

@ -4,33 +4,36 @@ Shindo.tests('Fog::Rackspace::Database | database_tests', ['rackspace']) do
service = Fog::Rackspace::Databases.new service = Fog::Rackspace::Databases.new
instance_name = 'fog' + Time.now.to_i.to_s 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' begin
sleep 10 @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 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 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_name = 'fog' + Time.now.to_i.to_s
instance_id = service.create_instance(instance_name, 1, 1).body['instance']['id'] instance_id = service.create_instance(instance_name, 1, 1).body['instance']['id']
until service.get_instance(instance_id).body["instance"]["status"] == 'ACTIVE' wait_for_request("Waiting for database to be created") do
sleep 10 service.get_instance(instance_id).body["instance"]["status"] == 'ACTIVE'
end end
tests('success') do tests('success') do
user_name = 'fog' + Time.now.to_i.to_s user_name = 'fog' + Time.now.to_i.to_s
password = 'password1' password = 'password1'
tests("#create_user(#{instance_id}, #{user_name}, #{password})").succeeds do tests("#create_user(#{instance_id}, #{user_name}, #{password})").returns(202) do
service.create_user(instance_id, user_name, password).body service.create_user(instance_id, user_name, password).status
end end
tests("#list_users{#{instance_id})").formats(LIST_USERS_FORMAT) do tests("#list_users{#{instance_id})").formats(LIST_USERS_FORMAT) do
service.list_users(instance_id).body service.list_users(instance_id).body
end end
tests("#delete_user(#{instance_id}, #{user_name})").succeeds do tests("#delete_user(#{instance_id}, #{user_name})").returns(202) do
service.delete_user(instance_id, user_name) service.delete_user(instance_id, user_name).status
end end
end end

View file

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

View file

@ -60,7 +60,8 @@ Shindo.tests('Fog::Compute[:xenserver] | server model', ['xenserver']) do
:pv_kernel, :pv_kernel,
:pv_ramdisk, :pv_ramdisk,
:pv_legacy_args, :pv_legacy_args,
:pv_bootloader_args :pv_bootloader_args,
:snapshots
] ]
tests("The server model should respond to") do tests("The server model should respond to") do
attributes.each do |attribute| attributes.each do |attribute|
@ -157,6 +158,15 @@ Shindo.tests('Fog::Compute[:xenserver] | server model', ['xenserver']) do
server.pv_bootloader == 'supergrub' server.pv_bootloader == 'supergrub'
end 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 test("be able to be destroyed!") do
server.destroy server.destroy
servers.get_by_name('fog-test-server-shindo') == nil servers.get_by_name('fog-test-server-shindo') == nil