mirror of
https://github.com/fog/fog.git
synced 2022-11-09 13:51:43 -05:00
Merge branch 'master'
This commit is contained in:
commit
07b0b704b5
80 changed files with 2351 additions and 501 deletions
|
@ -21,6 +21,7 @@ Gem::Specification.new do |s|
|
|||
s.authors = ["geemus (Wesley Beary)"]
|
||||
s.email = 'geemus@gmail.com'
|
||||
s.homepage = 'http://github.com/fog/fog'
|
||||
s.license = 'MIT'
|
||||
|
||||
## This sections is only necessary if you have C extensions.
|
||||
# s.require_paths << 'ext'
|
||||
|
|
26
lib/fog/aws/models/rds/log_file.rb
Normal file
26
lib/fog/aws/models/rds/log_file.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
require 'fog/core/model'
|
||||
|
||||
module Fog
|
||||
module AWS
|
||||
class RDS
|
||||
|
||||
class LogFile < Fog::Model
|
||||
|
||||
attribute :rds_id, :aliases => 'DBInstanceIdentifier'
|
||||
attribute :name, :aliases => 'LogFileName'
|
||||
attribute :size, :aliases => 'Size', :type => :integer
|
||||
attribute :last_written, :aliases => 'LastWritten', :type => :time
|
||||
attribute :content, :aliases => 'LogFileData'
|
||||
attribute :marker, :aliases => 'Marker'
|
||||
attribute :more_content_available, :aliases => 'AdditionalDataPending', :type => :boolean
|
||||
|
||||
def content_excerpt(marker=nil)
|
||||
result = service.download_db_logfile_portion(self.rds_id, self.name, {:marker => marker})
|
||||
merge_attributes(result.body['DownloadDBLogFilePortionResult'])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
50
lib/fog/aws/models/rds/log_files.rb
Normal file
50
lib/fog/aws/models/rds/log_files.rb
Normal file
|
@ -0,0 +1,50 @@
|
|||
require 'fog/core/collection'
|
||||
require 'fog/aws/models/rds/log_file'
|
||||
|
||||
module Fog
|
||||
module AWS
|
||||
class RDS
|
||||
|
||||
class LogFiles < Fog::Collection
|
||||
attribute :filters
|
||||
attribute :rds_id
|
||||
model Fog::AWS::RDS::LogFile
|
||||
|
||||
def initialize(attributes)
|
||||
self.filters ||= {}
|
||||
super
|
||||
end
|
||||
|
||||
# This method deliberately returns only a single page of results
|
||||
def all(filters=filters)
|
||||
self.filters.merge!(filters)
|
||||
|
||||
result = service.describe_db_log_files(rds_id, self.filters).body['DescribeDBLogFilesResult']
|
||||
self.filters[:marker] = result['Marker']
|
||||
load(result['DBLogFiles'])
|
||||
end
|
||||
|
||||
def each(filters=filters)
|
||||
if block_given?
|
||||
begin
|
||||
page = self.all(filters)
|
||||
# We need to explicitly use the base 'each' method here on the page, otherwise we get infinite recursion
|
||||
base_each = Fog::Collection.instance_method(:each)
|
||||
base_each.bind(page).call { |log_file| yield log_file }
|
||||
end while self.filters[:marker]
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def get(file_name=nil)
|
||||
if file_name
|
||||
matches = self.select {|log_file| log_file.name.upcase == file_name.upcase}
|
||||
return matches.first unless matches.empty?
|
||||
end
|
||||
rescue Fog::AWS::RDS::NotFound
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -21,26 +21,33 @@ module Fog
|
|||
super
|
||||
end
|
||||
|
||||
# This will return a single page based on the current or provided filters,
|
||||
# updating the filters with the marker for the next page. Calling this repeatedly
|
||||
# will iterate through pages.
|
||||
# This method does NOT return all snapshots. Its implementation deliberately returns a single page
|
||||
# of results for any one call. It will return a single page based on the current or provided filters,
|
||||
# updating the filters with the marker for the next page. Calling this repeatedly will iterate
|
||||
# through pages. See the implementation of each for an example of such iteration.
|
||||
#
|
||||
# It is arguably incorrect for the method not to return all snapshots, particularly considering the
|
||||
# implementation in the corresponding 'elb' files. But this implementation has been released, and
|
||||
# backwards-compatibility requires leaving it as implemented.
|
||||
def all(filters = filters)
|
||||
self.filters.merge!(filters)
|
||||
|
||||
snapshots = service.describe_db_snapshots(filters)
|
||||
self.filters[:marker] = snapshots.body['DescribeDBSnapshotsResult']['Marker']
|
||||
data = snapshots.body['DescribeDBSnapshotsResult']['DBSnapshots']
|
||||
load(data)
|
||||
page = service.describe_db_snapshots(self.filters).body['DescribeDBSnapshotsResult']
|
||||
self.filters[:marker] = page['Marker']
|
||||
load(page['DBSnapshots'])
|
||||
end
|
||||
|
||||
# This will execute a block for each snapshot, fetching new pages of snapshots as required.
|
||||
def each(filters = filters)
|
||||
begin
|
||||
page = self.all(filters)
|
||||
# We need to explicitly use the base 'each' method here on the page, otherwise we get infinite recursion
|
||||
base_each = Fog::Collection.instance_method(:each)
|
||||
base_each.bind(page).call {|snapshot| yield snapshot}
|
||||
end while self.filters[:marker]
|
||||
if block_given?
|
||||
begin
|
||||
page = self.all(filters)
|
||||
# We need to explicitly use the base 'each' method here on the page, otherwise we get infinite recursion
|
||||
base_each = Fog::Collection.instance_method(:each)
|
||||
base_each.bind(page).call { |snapshot| yield snapshot }
|
||||
end while self.filters[:marker]
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def get(identity)
|
||||
|
|
44
lib/fog/aws/parsers/rds/describe_db_log_files.rb
Normal file
44
lib/fog/aws/parsers/rds/describe_db_log_files.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
module Fog
|
||||
module Parsers
|
||||
module AWS
|
||||
module RDS
|
||||
|
||||
class DescribeDBLogFiles < Fog::Parsers::Base
|
||||
|
||||
attr_reader :rds_id
|
||||
|
||||
def initialize(rds_id)
|
||||
@rds_id = rds_id
|
||||
super()
|
||||
end
|
||||
|
||||
def reset
|
||||
@response = { 'DescribeDBLogFilesResult' => {'DBLogFiles' => []}, 'ResponseMetadata' => {} }
|
||||
fresh_log_file
|
||||
end
|
||||
|
||||
def fresh_log_file
|
||||
@db_log_file = {'DBInstanceIdentifier' => @rds_id}
|
||||
end
|
||||
|
||||
def start_element(name, attrs = [])
|
||||
super
|
||||
end
|
||||
|
||||
def end_element(name)
|
||||
case name
|
||||
when 'LastWritten' then @db_log_file[name] = Time.at(value.to_i / 1000)
|
||||
when 'LogFileName' then @db_log_file[name] = value
|
||||
when 'Size' then @db_log_file[name] = value.to_i
|
||||
when 'DescribeDBLogFilesDetails'
|
||||
@response['DescribeDBLogFilesResult']['DBLogFiles'] << @db_log_file
|
||||
fresh_log_file
|
||||
when 'Marker' then @response['DescribeDBLogFilesResult'][name] = value
|
||||
when 'RequestId' then @response['ResponseMetadata'][name] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
26
lib/fog/aws/parsers/rds/download_db_logfile_portion.rb
Normal file
26
lib/fog/aws/parsers/rds/download_db_logfile_portion.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
module Fog
|
||||
module Parsers
|
||||
module AWS
|
||||
module RDS
|
||||
|
||||
class DownloadDBLogFilePortion < Fog::Parsers::Base
|
||||
|
||||
def reset
|
||||
@response = { 'DownloadDBLogFilePortionResult' => {}, 'ResponseMetadata' => {} }
|
||||
end
|
||||
|
||||
def start_element(name, attrs = [])
|
||||
super
|
||||
end
|
||||
|
||||
def end_element(name)
|
||||
key = (name == 'RequestId') ? 'ResponseMetadata' : 'DownloadDBLogFilePortionResult'
|
||||
@response[key][name] = value
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
30
lib/fog/aws/parsers/sts/assume_role.rb
Normal file
30
lib/fog/aws/parsers/sts/assume_role.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
module Fog
|
||||
module Parsers
|
||||
module AWS
|
||||
module STS
|
||||
|
||||
class AssumeRole < Fog::Parsers::Base
|
||||
|
||||
def reset
|
||||
@response = {}
|
||||
end
|
||||
|
||||
def end_element(name)
|
||||
case name
|
||||
when 'SessionToken', 'SecretAccessKey', 'Expiration', 'AccessKeyId'
|
||||
@response[name] = @value.strip
|
||||
when 'Arn', 'AssumedRoleId'
|
||||
@response[name] = @value.strip
|
||||
when 'PackedPolicySize'
|
||||
@response[name] = @value
|
||||
when 'RequestId'
|
||||
@response[name] = @value
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -53,6 +53,9 @@ module Fog
|
|||
|
||||
request :describe_orderable_db_instance_options
|
||||
|
||||
request :describe_db_log_files
|
||||
request :download_db_logfile_portion
|
||||
|
||||
model_path 'fog/aws/models/rds'
|
||||
model :server
|
||||
collection :servers
|
||||
|
@ -75,6 +78,9 @@ module Fog
|
|||
model :instance_option
|
||||
collection :instance_options
|
||||
|
||||
model :log_file
|
||||
collection :log_files
|
||||
|
||||
class Mock
|
||||
|
||||
def self.data
|
||||
|
|
67
lib/fog/aws/requests/rds/describe_db_log_files.rb
Normal file
67
lib/fog/aws/requests/rds/describe_db_log_files.rb
Normal file
|
@ -0,0 +1,67 @@
|
|||
module Fog
|
||||
module AWS
|
||||
class RDS
|
||||
class Real
|
||||
|
||||
require 'fog/aws/parsers/rds/describe_db_log_files'
|
||||
|
||||
# Describe log files for a DB instance
|
||||
# http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference/API_DescribeDBLogFiles.html
|
||||
# ==== Parameters
|
||||
# * DBInstanceIdentifier <~String> - ID of instance to retrieve information for. Required.
|
||||
# * Options <~Hash> - Hash of options. Optional. The following keys are used:
|
||||
# * :file_last_written <~Long> - Filter available log files for those written after this time. Optional.
|
||||
# * :file_size <~Long> - Filters the available log files for files larger than the specified size. Optional.
|
||||
# * :filename_contains <~String> - Filters the available log files for log file names that contain the specified string. Optional.
|
||||
# * :marker <~String> - The pagination token provided in the previous request. If this parameter is specified the response includes only records beyond the marker, up to MaxRecords. Optional.
|
||||
# * :max_records <~Integer> - The maximum number of records to include in the response. If more records exist, a pagination token is included in the response. Optional.
|
||||
# ==== Returns
|
||||
# * response<~Excon::Response>:
|
||||
# * body<~Hash>:
|
||||
def describe_db_log_files(rds_id=nil, opts={})
|
||||
params = {}
|
||||
params['DBInstanceIdentifier'] = rds_id if rds_id
|
||||
params['Marker'] = opts[:marker] if opts[:marker]
|
||||
params['MaxRecords'] = opts[:max_records] if opts[:max_records]
|
||||
params['FilenameContains'] = opts[:filename_contains] if opts[:filename_contains]
|
||||
params['FileSize'] = opts[:file_size] if opts[:file_size]
|
||||
params['FileLastWritten'] = opts[:file_last_written] if opts[:file_last_written]
|
||||
|
||||
request({
|
||||
'Action' => 'DescribeDBLogFiles',
|
||||
:parser => Fog::Parsers::AWS::RDS::DescribeDBLogFiles.new(rds_id)
|
||||
}.merge(params))
|
||||
end
|
||||
end
|
||||
|
||||
class Mock
|
||||
|
||||
def describe_db_log_files(rds_id=nil, opts={})
|
||||
response = Excon::Response.new
|
||||
log_file_set = []
|
||||
|
||||
if rds_id
|
||||
if server = self.data[:servers][rds_id]
|
||||
log_file_set << {"LastWritten" => Time.parse('2013-07-05 17:00:00 -0700'), "LogFileName" => "error/mysql-error.log", "Size" => 0}
|
||||
log_file_set << {"LastWritten" => Time.parse('2013-07-05 17:10:00 -0700'), "LogFileName" => "error/mysql-error-running.log", "Size" => 0}
|
||||
log_file_set << {"LastWritten" => Time.parse('2013-07-05 17:20:00 -0700'), "LogFileName" => "error/mysql-error-running.log.0", "Size" => 8220}
|
||||
log_file_set << {"LastWritten" => Time.parse('2013-07-05 17:30:00 -0700'), "LogFileName" => "error/mysql-error-running.log.1", "Size" => 0}
|
||||
else
|
||||
raise Fog::AWS::RDS::NotFound.new("DBInstance #{rds_id} not found")
|
||||
end
|
||||
else
|
||||
raise Fog::AWS::RDS::NotFound.new('An identifier for an RDS instance must be provided')
|
||||
end
|
||||
|
||||
response.status = 200
|
||||
response.body = {
|
||||
"ResponseMetadata" => { "RequestId" => Fog::AWS::Mock.request_id },
|
||||
"DescribeDBLogFilesResult" => { "DBLogFiles" => log_file_set }
|
||||
}
|
||||
response
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
63
lib/fog/aws/requests/rds/download_db_logfile_portion.rb
Normal file
63
lib/fog/aws/requests/rds/download_db_logfile_portion.rb
Normal file
|
@ -0,0 +1,63 @@
|
|||
module Fog
|
||||
module AWS
|
||||
class RDS
|
||||
class Real
|
||||
|
||||
require 'fog/aws/parsers/rds/download_db_logfile_portion'
|
||||
|
||||
# Retrieve a portion of a log file of a db instance
|
||||
# http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference/API_DownloadDBLogFilePortion.html
|
||||
# ==== Parameters
|
||||
# * DBInstanceIdentifier <~String> - ID of instance to retrieve information for. Required.
|
||||
# * LogFileName <~String> - The name of the log file to be downloaded. Required.
|
||||
# * Options <~Hash> - Hash of options. Optional. The following keys are used:
|
||||
# * :marker <~String> - The pagination token provided in the previous request. If this parameter is specified the response includes only records beyond the marker, up to MaxRecords. Optional.
|
||||
# * :max_records <~Integer> - The maximum number of records to include in the response. If more records exist, a pagination token is included in the response. Optional.
|
||||
# * :number_of_lines <~Integer> - The number of lines to download. Optional.
|
||||
# ==== Returns
|
||||
# * response<~Excon::Response>:
|
||||
# * body<~Hash>:
|
||||
def download_db_logfile_portion(identifier=nil, filename=nil, opts={})
|
||||
params = {}
|
||||
params['DBInstanceIdentifier'] = identifier if identifier
|
||||
params['LogFileName'] = filename if filename
|
||||
params['Marker'] = opts[:marker] if opts[:marker]
|
||||
params['MaxRecords'] = opts[:max_records] if opts[:max_records]
|
||||
params['NumberOfLines'] = opts[:number_of_lines] if opts[:number_of_lines]
|
||||
|
||||
request({
|
||||
'Action' => 'DownloadDBLogFilePortion',
|
||||
:parser => Fog::Parsers::AWS::RDS::DownloadDBLogFilePortion.new
|
||||
}.merge(params))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Mock
|
||||
|
||||
def download_db_logfile_portion(identifier=nil, filename=nil, opts={})
|
||||
response = Excon::Response.new
|
||||
server_set = []
|
||||
if identifier
|
||||
if server = self.data[:servers][identifier]
|
||||
server_set << server
|
||||
else
|
||||
raise Fog::AWS::RDS::NotFound.new("DBInstance #{identifier} not found")
|
||||
end
|
||||
else
|
||||
server_set = self.data[:servers].values
|
||||
end
|
||||
|
||||
response.status = 200
|
||||
response.body = {
|
||||
"ResponseMetadata" => { "RequestId"=> Fog::AWS::Mock.request_id },
|
||||
"DescribeDBInstancesResult" => { "DBInstances" => server_set }
|
||||
}
|
||||
response
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -30,7 +30,7 @@ module Fog
|
|||
|
||||
request({
|
||||
'Action' => 'SendRawEmail',
|
||||
'RawMessage.Data' => Base64.encode64(raw_message).chomp!,
|
||||
'RawMessage.Data' => Base64.encode64(raw_message.to_s).chomp!,
|
||||
:parser => Fog::Parsers::AWS::SES::SendRawEmail.new
|
||||
}.merge(params))
|
||||
end
|
||||
|
|
|
@ -87,7 +87,7 @@ module Fog
|
|||
if (object && !object[:delete_marker])
|
||||
if options['If-Match'] && options['If-Match'] != object['ETag']
|
||||
response.status = 412
|
||||
elsif options['If-Modified-Since'] && options['If-Modified-Since'] > Time.parse(object['Last-Modified'])
|
||||
elsif options['If-Modified-Since'] && options['If-Modified-Since'] >= Time.parse(object['Last-Modified'])
|
||||
response.status = 304
|
||||
elsif options['If-None-Match'] && options['If-None-Match'] == object['ETag']
|
||||
response.status = 304
|
||||
|
|
46
lib/fog/aws/requests/sts/assume_role.rb
Normal file
46
lib/fog/aws/requests/sts/assume_role.rb
Normal file
|
@ -0,0 +1,46 @@
|
|||
module Fog
|
||||
module AWS
|
||||
class STS
|
||||
class Real
|
||||
|
||||
require 'fog/aws/parsers/sts/assume_role'
|
||||
|
||||
# Assume Role
|
||||
#
|
||||
# ==== Parameters
|
||||
# * role_session_name<~String> - An identifier for the assumed role.
|
||||
# * role_arn<~String> - The ARN of the role the caller is assuming.
|
||||
# * external_id<~String> - An optional unique identifier required by the assuming role's trust identity.
|
||||
# * policy<~String> - An optional JSON policy document
|
||||
# * duration<~Integer> - Duration (of seconds) for the assumed role credentials to be valid (default 3600)
|
||||
#
|
||||
# ==== Returns
|
||||
# * response<~Excon::Response>:
|
||||
# * body<~Hash>:
|
||||
# * 'Arn'<~String>: The ARN of the assumed role/user
|
||||
# * 'AccessKeyId'<~String>: The AWS access key of the temporary credentials for the assumed role
|
||||
# * 'SecretAccessKey'<~String>: The AWS secret key of the temporary credentials for the assumed role
|
||||
# * 'SessionToken'<~String>: The AWS session token of the temporary credentials for the assumed role
|
||||
# * 'Expiration'<~Time>: The expiration time of the temporary credentials for the assumed role
|
||||
#
|
||||
# ==== See Also
|
||||
# http://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html
|
||||
#
|
||||
|
||||
def assume_role(role_session_name, role_arn, external_id=nil, policy=nil, duration=3600)
|
||||
request({
|
||||
'Action' => 'AssumeRole',
|
||||
'RoleSessionName' => role_session_name,
|
||||
'RoleArn' => role_arn,
|
||||
'Policy' => policy && Fog::JSON.encode(policy),
|
||||
'DurationSeconds' => duration,
|
||||
'ExternalId' => external_id,
|
||||
:idempotent => true,
|
||||
:parser => Fog::Parsers::AWS::STS::AssumeRole.new
|
||||
})
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,6 +5,9 @@ module Fog
|
|||
class SES < Fog::Service
|
||||
extend Fog::AWS::CredentialFetcher::ServiceMethods
|
||||
|
||||
class InvalidParameterError < Fog::Errors::Error; end
|
||||
class MessageRejected < Fog::Errors::Error; end
|
||||
|
||||
requires :aws_access_key_id, :aws_secret_access_key
|
||||
recognizes :region, :host, :path, :port, :scheme, :persistent, :use_iam_profile, :aws_session_token, :aws_credentials_expire_at
|
||||
|
||||
|
@ -103,15 +106,28 @@ module Fog
|
|||
end
|
||||
body.chop! # remove trailing '&'
|
||||
|
||||
response = @connection.request({
|
||||
:body => body,
|
||||
:expects => 200,
|
||||
:headers => headers,
|
||||
:idempotent => idempotent,
|
||||
:host => @host,
|
||||
:method => 'POST',
|
||||
:parser => parser
|
||||
})
|
||||
begin
|
||||
response = @connection.request({
|
||||
:body => body,
|
||||
:expects => 200,
|
||||
:headers => headers,
|
||||
:idempotent => idempotent,
|
||||
:host => @host,
|
||||
:method => 'POST',
|
||||
:parser => parser
|
||||
})
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
match = Fog::AWS::Errors.match_error(error)
|
||||
raise if match.empty?
|
||||
raise case match[:code]
|
||||
when 'MessageRejected'
|
||||
Fog::AWS::SES::MessageRejected.slurp(error, match[:message])
|
||||
when 'InvalidParameterValue'
|
||||
Fog::AWS::SES::InvalidParameterError.slurp(error, match[:message])
|
||||
else
|
||||
Fog::AWS::SES::Error.slurp(error, "#{match[:code]} => #{match[:message]}")
|
||||
end
|
||||
end
|
||||
|
||||
response
|
||||
end
|
||||
|
|
|
@ -3,16 +3,18 @@ require 'fog/aws'
|
|||
module Fog
|
||||
module AWS
|
||||
class STS < Fog::Service
|
||||
extend Fog::AWS::CredentialFetcher::ServiceMethods
|
||||
|
||||
class EntityAlreadyExists < Fog::AWS::STS::Error; end
|
||||
class ValidationError < Fog::AWS::STS::Error; end
|
||||
|
||||
requires :aws_access_key_id, :aws_secret_access_key
|
||||
recognizes :host, :path, :port, :scheme, :persistent
|
||||
recognizes :host, :path, :port, :scheme, :persistent, :aws_session_token, :use_iam_profile, :aws_credentials_expire_at
|
||||
|
||||
request_path 'fog/aws/requests/sts'
|
||||
request :get_federation_token
|
||||
request :get_session_token
|
||||
request :assume_role
|
||||
|
||||
class Mock
|
||||
def self.data
|
||||
|
@ -33,7 +35,8 @@ module Fog
|
|||
end
|
||||
|
||||
def initialize(options={})
|
||||
@aws_access_key_id = options[:aws_access_key_id]
|
||||
@use_iam_profile = options[:use_iam_profile]
|
||||
setup_credentials(options)
|
||||
end
|
||||
|
||||
def data
|
||||
|
@ -43,10 +46,14 @@ module Fog
|
|||
def reset_data
|
||||
self.class.data.delete(@aws_access_key_id)
|
||||
end
|
||||
|
||||
def setup_credentials(options)
|
||||
@aws_access_key_id = options[:aws_access_key_id]
|
||||
end
|
||||
end
|
||||
|
||||
class Real
|
||||
|
||||
include Fog::AWS::CredentialFetcher::ConnectionMethods
|
||||
# Initialize connection to STS
|
||||
#
|
||||
# ==== Notes
|
||||
|
@ -67,10 +74,10 @@ module Fog
|
|||
def initialize(options={})
|
||||
require 'fog/core/parser'
|
||||
|
||||
@aws_access_key_id = options[:aws_access_key_id]
|
||||
@aws_secret_access_key = options[:aws_secret_access_key]
|
||||
@use_iam_profile = options[:use_iam_profile]
|
||||
setup_credentials(options)
|
||||
@connection_options = options[:connection_options] || {}
|
||||
@hmac = Fog::HMAC.new('sha256', @aws_secret_access_key)
|
||||
|
||||
@host = options[:host] || 'sts.amazonaws.com'
|
||||
@path = options[:path] || '/'
|
||||
@persistent = options[:persistent] || false
|
||||
|
@ -85,6 +92,14 @@ module Fog
|
|||
|
||||
private
|
||||
|
||||
def setup_credentials(options)
|
||||
@aws_access_key_id = options[:aws_access_key_id]
|
||||
@aws_secret_access_key = options[:aws_secret_access_key]
|
||||
@aws_session_token = options[:aws_session_token]
|
||||
@aws_credentials_expire_at = options[:aws_credentials_expire_at]
|
||||
@hmac = Fog::HMAC.new('sha256', @aws_secret_access_key)
|
||||
end
|
||||
|
||||
def request(params)
|
||||
idempotent = params.delete(:idempotent)
|
||||
parser = params.delete(:parser)
|
||||
|
@ -93,6 +108,7 @@ module Fog
|
|||
params,
|
||||
{
|
||||
:aws_access_key_id => @aws_access_key_id,
|
||||
:aws_session_token => @aws_session_token,
|
||||
:hmac => @hmac,
|
||||
:host => @host,
|
||||
:path => @path,
|
||||
|
|
|
@ -15,6 +15,8 @@ class OpenStack < Fog::Bin
|
|||
Fog::Storage::OpenStack
|
||||
when :volume
|
||||
Fog::Volume::OpenStack
|
||||
when :metering
|
||||
Fog::Metering::OpenStack
|
||||
else
|
||||
raise ArgumentError, "Unrecognized service: #{key}"
|
||||
end
|
||||
|
@ -41,6 +43,9 @@ class OpenStack < Fog::Bin
|
|||
when :volume
|
||||
Fog::Logger.warning("OpenStack[:volume] is not recommended, use Volume[:openstack] for portability")
|
||||
Fog::Volume.new(:provider => 'OpenStack')
|
||||
when :metering
|
||||
Fog::Logger.warning("OpenStack[:metering] is not recommended, use Metering[:openstack] for portability")
|
||||
Fog::Metering.new(:provider => 'OpenStack')
|
||||
else
|
||||
raise ArgumentError, "Unrecognized service: #{key.inspect}"
|
||||
end
|
||||
|
|
|
@ -53,6 +53,9 @@ module Fog
|
|||
model :flavor
|
||||
collection :flavors
|
||||
|
||||
model :disk
|
||||
collection :disks
|
||||
|
||||
class Mock
|
||||
include Collections
|
||||
|
||||
|
@ -126,7 +129,7 @@ module Fog
|
|||
response.status = response.body["error"]["code"]
|
||||
|
||||
response.body["error"]["errors"].each do |error|
|
||||
throw Fog::Errors::Error.new(error["message"])
|
||||
raise Fog::Errors::Error.new(error["message"])
|
||||
end
|
||||
else
|
||||
response.status = 200
|
||||
|
@ -134,6 +137,22 @@ module Fog
|
|||
response
|
||||
end
|
||||
|
||||
def backoff_if_unfound(&block)
|
||||
retries_remaining = 10
|
||||
begin
|
||||
result = block.call
|
||||
rescue Exception => msg
|
||||
if msg.to_s.include? 'was not found' and retries_remaining > 0
|
||||
retries_remaining -= 1
|
||||
sleep 0.1
|
||||
retry
|
||||
else
|
||||
raise msg
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
RUNNING = 'RUNNING'
|
||||
|
|
80
lib/fog/google/models/compute/disk.rb
Normal file
80
lib/fog/google/models/compute/disk.rb
Normal file
|
@ -0,0 +1,80 @@
|
|||
require 'fog/core/model'
|
||||
|
||||
module Fog
|
||||
module Compute
|
||||
class Google
|
||||
|
||||
class Disk < Fog::Model
|
||||
|
||||
identity :name
|
||||
|
||||
attribute :kind, :aliases => 'kind'
|
||||
attribute :id, :aliases => 'id'
|
||||
attribute :creation_timestamp, :aliases => 'creationTimestamp'
|
||||
attribute :zone_name, :aliases => 'zone'
|
||||
attribute :status, :aliases => 'status'
|
||||
attribute :description, :aliases => 'description'
|
||||
attribute :size_gb, :aliases => 'sizeGb'
|
||||
attribute :self_link, :aliases => 'selfLink'
|
||||
attribute :image_name, :aliases => 'image'
|
||||
|
||||
def save
|
||||
data = service.insert_disk(name, size_gb, zone_name, image_name).body
|
||||
data = service.backoff_if_unfound {service.get_disk(name, zone_name).body}
|
||||
service.disks.merge_attributes(data)
|
||||
end
|
||||
|
||||
def destroy
|
||||
requires :name, :zone
|
||||
service.delete_disk(name, zone)
|
||||
end
|
||||
|
||||
def zone
|
||||
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 get_as_boot_disk(writable=true)
|
||||
mode = writable ? 'READ_WRITE' : 'READ_ONLY'
|
||||
return {
|
||||
'name' => name,
|
||||
'type' => 'PERSISTENT',
|
||||
'boot' => true,
|
||||
'source' => self_link,
|
||||
'mode' => mode
|
||||
}
|
||||
end
|
||||
|
||||
def ready?
|
||||
data = service.get_disk(self.name, self.zone_name).body
|
||||
data['zone_name'] = self.zone_name
|
||||
self.merge_attributes(data)
|
||||
self.status == RUNNING_STATE
|
||||
end
|
||||
|
||||
def reload
|
||||
requires :identity
|
||||
requires :zone_name
|
||||
|
||||
return unless data = begin
|
||||
collection.get(identity, zone_name)
|
||||
rescue Excon::Errors::SocketError
|
||||
nil
|
||||
end
|
||||
|
||||
new_attributes = data.attributes
|
||||
merge_attributes(new_attributes)
|
||||
self
|
||||
end
|
||||
|
||||
RUNNING_STATE = "READY"
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
28
lib/fog/google/models/compute/disks.rb
Normal file
28
lib/fog/google/models/compute/disks.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
require 'fog/core/collection'
|
||||
require 'fog/google/models/compute/disk'
|
||||
|
||||
module Fog
|
||||
module Compute
|
||||
class Google
|
||||
|
||||
class Disks < Fog::Collection
|
||||
|
||||
model Fog::Compute::Google::Disk
|
||||
|
||||
def all(zone)
|
||||
data = service.list_disks(zone).body["items"]
|
||||
load(data)
|
||||
end
|
||||
|
||||
def get(identity, zone)
|
||||
data = service.get_disk(identity, zone).body
|
||||
new(data)
|
||||
rescue Excon::Errors::NotFound
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -18,19 +18,24 @@ module Fog
|
|||
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
|
||||
|
||||
# Try looking for the image in known projects
|
||||
[
|
||||
self.service.project,
|
||||
'google',
|
||||
'debian-cloud',
|
||||
'centos-cloud',
|
||||
].each do |owner|
|
||||
begin
|
||||
data = service.get_image(name, owner).body
|
||||
data[:project] = owner
|
||||
rescue
|
||||
end
|
||||
end
|
||||
self.merge_attributes(data)
|
||||
|
||||
raise ArgumentError, 'Specified image was not found' if data.empty?
|
||||
|
||||
self.merge_attributes(data)
|
||||
self
|
||||
end
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ module Fog
|
|||
attribute :state, :aliases => 'status'
|
||||
attribute :zone_name, :aliases => 'zone'
|
||||
attribute :machine_type, :aliases => 'machineType'
|
||||
attribute :disks, :aliases => 'disks'
|
||||
attribute :kernel, :aliases => 'kernel'
|
||||
attribute :metadata
|
||||
|
||||
def destroy
|
||||
|
@ -60,24 +62,29 @@ module Fog
|
|||
|
||||
def save
|
||||
requires :name
|
||||
requires :image_name
|
||||
requires :machine_type
|
||||
requires :zone_name
|
||||
|
||||
if metadata.nil?
|
||||
metadata = {}
|
||||
if self.metadata.nil?
|
||||
self.metadata = {}
|
||||
end
|
||||
|
||||
metadata.merge!({
|
||||
self.metadata.merge!({
|
||||
"sshKeys" => "#{username}:#{File.read(public_key_path).strip}"
|
||||
}) if :public_key_path
|
||||
}) if public_key_path
|
||||
|
||||
data = service.insert_server(
|
||||
name,
|
||||
image_name,
|
||||
zone_name,
|
||||
machine_type,
|
||||
metadata)
|
||||
options = {
|
||||
'image' => image_name,
|
||||
'machineType' => machine_type,
|
||||
'networkInterfaces' => network_interfaces,
|
||||
'disks' => disks,
|
||||
'kernel' => kernel,
|
||||
'metadata' => metadata
|
||||
}
|
||||
options.delete_if {|key, value| value.nil?}
|
||||
service.insert_server(name, zone_name, options)
|
||||
data = service.backoff_if_unfound {service.get_server(self.name, self.zone_name).body}
|
||||
service.servers.merge_attributes(data)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -51,6 +51,11 @@ module Fog
|
|||
:public_key_path => File.expand_path("~/.ssh/id_rsa.pub"),
|
||||
:username => ENV['USER'],
|
||||
}
|
||||
if new_attributes[:disks]
|
||||
new_attributes[:disks].each do |disk|
|
||||
defaults.delete :image_name if disk['boot']
|
||||
end
|
||||
end
|
||||
|
||||
server = create(defaults.merge(new_attributes))
|
||||
server.wait_for { sshable? }
|
||||
|
|
|
@ -13,6 +13,9 @@ module Fog
|
|||
class Real
|
||||
|
||||
def get_disk(disk_name, zone_name=@default_zone)
|
||||
if zone_name.start_with? 'http'
|
||||
zone_name = zone_name.split('/')[-1]
|
||||
end
|
||||
api_method = @compute.disks.get
|
||||
parameters = {
|
||||
'project' => @project,
|
||||
|
|
|
@ -12,19 +12,25 @@ module Fog
|
|||
|
||||
class Real
|
||||
|
||||
def insert_disk(disk_name, disk_size, zone_name=@default_zone)
|
||||
def insert_disk(disk_name, disk_size, zone_name=@default_zone, image_name=nil)
|
||||
api_method = @compute.disks.insert
|
||||
parameters = {
|
||||
'project' => @project,
|
||||
'zone' => zone_name
|
||||
}
|
||||
if image_name
|
||||
# We don't know the owner of the image.
|
||||
image = images.create({:name => image_name})
|
||||
@image_url = @api_url + image.resource_url
|
||||
parameters['sourceImage'] = @image_url
|
||||
end
|
||||
body_object = {
|
||||
'name' => disk_name,
|
||||
'sizeGb' => disk_size,
|
||||
}
|
||||
|
||||
result = self.build_result(api_method, parameters,
|
||||
body_object=body_object)
|
||||
body_object)
|
||||
response = self.build_response(result)
|
||||
end
|
||||
|
||||
|
|
|
@ -16,32 +16,57 @@ module Fog
|
|||
{ "items" => metadata.map {|k,v| {"key" => k, "value" => v}} }
|
||||
end
|
||||
|
||||
def insert_server(server_name, image_name,
|
||||
zone_name, machine_name, metadata,
|
||||
network_name=@default_network)
|
||||
|
||||
# We don't know the owner of the image.
|
||||
image = images.create({:name => image_name})
|
||||
@image_url = @api_url + image.resource_url
|
||||
|
||||
def insert_server(server_name, zone_name, options={}, *deprecated_args)
|
||||
if deprecated_args.length > 0 or not options.is_a? Hash
|
||||
raise ArgumentError.new 'Too many parameters specified. This may be the cause of code written for an outdated'\
|
||||
' version of fog. Usage: server_name, zone_name, [options]'
|
||||
end
|
||||
api_method = @compute.instances.insert
|
||||
parameters = {
|
||||
'project' => @project,
|
||||
'zone' => zone_name,
|
||||
}
|
||||
body_object = {
|
||||
'name' => server_name,
|
||||
'image' => @image_url,
|
||||
'machineType' => @api_url + @project + "/zones/#{zone_name}/machineTypes/#{machine_name}",
|
||||
'metadata' => format_metadata(metadata),
|
||||
'networkInterfaces' => [{
|
||||
'network' => @api_url + @project + "/global/networks/#{network_name}",
|
||||
'accessConfigs' => [{
|
||||
'type' => 'ONE_TO_ONE_NAT',
|
||||
'name' => 'External NAT',
|
||||
}]
|
||||
}]
|
||||
'project' => @project,
|
||||
'zone' => zone_name,
|
||||
}
|
||||
body_object = {:name => server_name}
|
||||
|
||||
if options.has_key? 'image'
|
||||
image_name = options.delete 'image'
|
||||
# We don't know the owner of the image.
|
||||
image = images.create({:name => image_name})
|
||||
@image_url = @api_url + image.resource_url
|
||||
body_object['image'] = @image_url
|
||||
end
|
||||
body_object['machineType'] = @api_url + @project + "/zones/#{zone_name}/machineTypes/#{options.delete 'machineType'}"
|
||||
networkInterfaces = []
|
||||
if @default_network
|
||||
networkInterfaces << {
|
||||
'network' => @api_url + @project + "/global/networks/#{@default_network}",
|
||||
'accessConfigs' => [
|
||||
{'type' => 'ONE_TO_ONE_NAT',
|
||||
'name' => 'External NAT'}
|
||||
]
|
||||
}
|
||||
end
|
||||
# TODO: add other networks
|
||||
body_object['networkInterfaces'] = networkInterfaces
|
||||
|
||||
if options['disks']
|
||||
disks = []
|
||||
options.delete('disks').each do |disk|
|
||||
if disk.is_a? Disk
|
||||
disks << disk.get_object
|
||||
else
|
||||
disks << disk
|
||||
end
|
||||
end
|
||||
body_object['disks'] = disks
|
||||
end
|
||||
|
||||
options['metadata'] = format_metadata options['metadata'] if options['metadata']
|
||||
|
||||
if options['kernel']
|
||||
body_object['kernel'] = @api_url + "google/global/kernels/#{options.delete 'kernel'}"
|
||||
end
|
||||
body_object.merge! options # Adds in all remaining options that weren't explicitly handled.
|
||||
|
||||
result = self.build_result(api_method, parameters,
|
||||
body_object=body_object)
|
||||
|
|
|
@ -73,7 +73,7 @@ module Fog
|
|||
if (bucket = self.data[:buckets][bucket_name]) && (object = bucket[:objects][object_name])
|
||||
if options['If-Match'] && options['If-Match'] != object['ETag']
|
||||
response.status = 412
|
||||
elsif options['If-Modified-Since'] && options['If-Modified-Since'] > Time.parse(object['Last-Modified'])
|
||||
elsif options['If-Modified-Since'] && options['If-Modified-Since'] >= Time.parse(object['Last-Modified'])
|
||||
response.status = 304
|
||||
elsif options['If-None-Match'] && options['If-None-Match'] == object['ETag']
|
||||
response.status = 304
|
||||
|
|
|
@ -43,7 +43,7 @@ module Fog
|
|||
if (object = container[:objects][object_name])
|
||||
if options['If-Match'] && options['If-Match'] != object['ETag']
|
||||
response.status = 412
|
||||
elsif options['If-Modified-Since'] && options['If-Modified-Since'] > Time.parse(object['Last-Modified'])
|
||||
elsif options['If-Modified-Since'] && options['If-Modified-Since'] >= Time.parse(object['Last-Modified'])
|
||||
response.status = 304
|
||||
elsif options['If-None-Match'] && options['If-None-Match'] == object['ETag']
|
||||
response.status = 304
|
||||
|
|
|
@ -15,7 +15,7 @@ module Fog
|
|||
# * :ip<~String> - The ID of a static IP address to associate with this instance
|
||||
# * :volume_id<~String> - The ID of a storage volume to associate with this instance
|
||||
# * :vlan_id<~String> - The ID of a Vlan offering to associate with this instance.
|
||||
# * :secondary_ip<~String> - The ID of a static IP address to associate with this instance as secondary IP
|
||||
# * :secondary_ip<~String> - Comma separated list of static IP IDs to associate with this instance.
|
||||
# * :is_mini_ephermal<~Boolean> - Whether or not the instance should be provisioned with the root segment only
|
||||
# * :configuration_data<~Hash> - Arbitrary name/value pairs defined by the image being created
|
||||
# * :anti_collocation_instance<~String> - The ID of an existing anti-collocated instance.
|
||||
|
@ -32,24 +32,29 @@ module Fog
|
|||
# * 'imageId'<~String>:
|
||||
# * 'launchTime'<~Integer>: epoch time in ms representing when the instance was launched
|
||||
def create_instance(name, image_id, instance_type, location, options={})
|
||||
body_data = {
|
||||
'name' => name,
|
||||
'imageID' => image_id,
|
||||
'instanceType' => instance_type,
|
||||
'location' => location,
|
||||
'publicKey' => options[:key_name],
|
||||
'ip' => options[:ip],
|
||||
'volumeID' => options[:volume_id],
|
||||
'vlanID' => options[:vlan_id],
|
||||
'isMiniEphemeral' => options[:is_mini_ephemeral],
|
||||
'Configuration Data' => options[:configuration_data],
|
||||
'antiCollocationInstance' => options[:anti_collocation_instance]
|
||||
}
|
||||
if options[:secondary_ip]
|
||||
options[:secondary_ip].split(',').map(&:strip).each_with_index do |n, idx|
|
||||
body_data.merge!({"secondary.ip.#{idx}" => n})
|
||||
end
|
||||
end
|
||||
request(
|
||||
:method => 'POST',
|
||||
:expects => 200,
|
||||
:path => '/instances',
|
||||
:body => {
|
||||
'name' => name,
|
||||
'imageID' => image_id,
|
||||
'instanceType' => instance_type,
|
||||
'location' => location,
|
||||
'publicKey' => options[:key_name],
|
||||
'ip' => options[:ip],
|
||||
'volumeID' => options[:volume_id],
|
||||
'vlanID' => options[:vlan_id],
|
||||
'SecondaryIP' => options[:secondary_ip],
|
||||
'isMiniEphemeral' => options[:is_mini_ephemeral],
|
||||
'Configuration Data' => options[:configuration_data],
|
||||
'antiCollocationInstance' => options[:anti_collocation_instance],
|
||||
}
|
||||
:body => body_data
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ module Fog
|
|||
if (object && !object[:delete_marker])
|
||||
if options['If-Match'] && options['If-Match'] != object['ETag']
|
||||
response.status = 412
|
||||
elsif options['If-Modified-Since'] && options['If-Modified-Since'] > Time.parse(object['Last-Modified'])
|
||||
elsif options['If-Modified-Since'] && options['If-Modified-Since'] >= Time.parse(object['Last-Modified'])
|
||||
response.status = 304
|
||||
elsif options['If-None-Match'] && options['If-None-Match'] == object['ETag']
|
||||
response.status = 304
|
||||
|
|
25
lib/fog/metering.rb
Normal file
25
lib/fog/metering.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
module Fog
|
||||
module Metering
|
||||
|
||||
def self.[](provider)
|
||||
self.new(:provider => provider)
|
||||
end
|
||||
|
||||
def self.new(attributes)
|
||||
attributes = attributes.dup # Prevent delete from having side effects
|
||||
provider = attributes.delete(:provider).to_s.downcase.to_sym
|
||||
if self.providers.include?(provider)
|
||||
require "fog/#{provider}/metering"
|
||||
return Fog::Metering.const_get(Fog.providers[provider]).new(attributes)
|
||||
end
|
||||
|
||||
raise ArgumentError.new("#{provider} has no identity service")
|
||||
end
|
||||
|
||||
def self.providers
|
||||
Fog.services[:metering]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -47,6 +47,7 @@ module Fog
|
|||
service(:network, 'openstack/network', 'Network')
|
||||
service(:storage, 'openstack/storage', 'Storage')
|
||||
service(:volume, 'openstack/volume', 'Volume')
|
||||
service(:metering, 'openstack/metering', 'Metering')
|
||||
|
||||
# legacy v1.0 style auth
|
||||
def self.authenticate_v1(options, connection_options = {})
|
||||
|
|
215
lib/fog/openstack/metering.rb
Normal file
215
lib/fog/openstack/metering.rb
Normal file
|
@ -0,0 +1,215 @@
|
|||
require 'fog/metering'
|
||||
require 'fog/openstack'
|
||||
|
||||
module Fog
|
||||
module Metering
|
||||
class OpenStack < Fog::Service
|
||||
|
||||
requires :openstack_auth_url
|
||||
recognizes :openstack_auth_token, :openstack_management_url, :persistent,
|
||||
:openstack_service_type, :openstack_service_name, :openstack_tenant,
|
||||
:openstack_api_key, :openstack_username,
|
||||
:current_user, :current_tenant,
|
||||
:openstack_endpoint_type
|
||||
|
||||
model_path 'fog/openstack/models/metering'
|
||||
|
||||
model :resource
|
||||
collection :resources
|
||||
|
||||
|
||||
request_path 'fog/openstack/requests/metering'
|
||||
|
||||
# Metering
|
||||
request :get_resource
|
||||
request :get_samples
|
||||
request :get_statistics
|
||||
request :list_meters
|
||||
request :list_resources
|
||||
|
||||
|
||||
class Mock
|
||||
def self.data
|
||||
@data ||= Hash.new do |hash, key|
|
||||
hash[key] = {
|
||||
:users => {},
|
||||
:tenants => {}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def self.reset
|
||||
@data = nil
|
||||
end
|
||||
|
||||
def initialize(options={})
|
||||
require 'multi_json'
|
||||
@openstack_username = options[:openstack_username]
|
||||
@openstack_tenant = options[:openstack_tenant]
|
||||
@openstack_auth_uri = URI.parse(options[:openstack_auth_url])
|
||||
|
||||
@auth_token = Fog::Mock.random_base64(64)
|
||||
@auth_token_expiration = (Time.now.utc + 86400).iso8601
|
||||
|
||||
management_url = URI.parse(options[:openstack_auth_url])
|
||||
management_url.port = 8776
|
||||
management_url.path = '/v1'
|
||||
@openstack_management_url = management_url.to_s
|
||||
|
||||
@data ||= { :users => {} }
|
||||
unless @data[:users].find {|u| u['name'] == options[:openstack_username]}
|
||||
id = Fog::Mock.random_numbers(6).to_s
|
||||
@data[:users][id] = {
|
||||
'id' => id,
|
||||
'name' => options[:openstack_username],
|
||||
'email' => "#{options[:openstack_username]}@mock.com",
|
||||
'tenantId' => Fog::Mock.random_numbers(6).to_s,
|
||||
'enabled' => true
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def data
|
||||
self.class.data[@openstack_username]
|
||||
end
|
||||
|
||||
def reset_data
|
||||
self.class.data.delete(@openstack_username)
|
||||
end
|
||||
|
||||
def credentials
|
||||
{ :provider => 'openstack',
|
||||
:openstack_auth_url => @openstack_auth_uri.to_s,
|
||||
:openstack_auth_token => @auth_token,
|
||||
:openstack_management_url => @openstack_management_url }
|
||||
end
|
||||
end
|
||||
|
||||
class Real
|
||||
attr_reader :current_user
|
||||
attr_reader :current_tenant
|
||||
|
||||
def initialize(options={})
|
||||
require 'multi_json'
|
||||
|
||||
@openstack_auth_token = options[:openstack_auth_token]
|
||||
|
||||
unless @openstack_auth_token
|
||||
missing_credentials = Array.new
|
||||
@openstack_api_key = options[:openstack_api_key]
|
||||
@openstack_username = options[:openstack_username]
|
||||
|
||||
missing_credentials << :openstack_api_key unless @openstack_api_key
|
||||
missing_credentials << :openstack_username unless @openstack_username
|
||||
raise ArgumentError, "Missing required arguments: #{missing_credentials.join(', ')}" unless missing_credentials.empty?
|
||||
end
|
||||
|
||||
@openstack_tenant = options[:openstack_tenant]
|
||||
@openstack_auth_uri = URI.parse(options[:openstack_auth_url])
|
||||
@openstack_management_url = options[:openstack_management_url]
|
||||
@openstack_must_reauthenticate = false
|
||||
@openstack_service_type = options[:openstack_service_type] || ['metering']
|
||||
@openstack_service_name = options[:openstack_service_name]
|
||||
|
||||
@openstack_endpoint_type = options[:openstack_endpoint_type] || 'adminURL'
|
||||
@connection_options = options[:connection_options] || {}
|
||||
|
||||
@current_user = options[:current_user]
|
||||
@current_tenant = options[:current_tenant]
|
||||
|
||||
authenticate
|
||||
|
||||
@persistent = options[:persistent] || false
|
||||
@connection = Fog::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent, @connection_options)
|
||||
end
|
||||
|
||||
def credentials
|
||||
{ :provider => 'openstack',
|
||||
:openstack_auth_url => @openstack_auth_uri.to_s,
|
||||
:openstack_auth_token => @auth_token,
|
||||
:openstack_management_url => @openstack_management_url,
|
||||
:current_user => @current_user,
|
||||
:current_tenant => @current_tenant }
|
||||
end
|
||||
|
||||
def reload
|
||||
@connection.reset
|
||||
end
|
||||
|
||||
def request(params)
|
||||
begin
|
||||
response = @connection.request(params.merge({
|
||||
:headers => {
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => 'application/json',
|
||||
'X-Auth-Token' => @auth_token
|
||||
}.merge!(params[:headers] || {}),
|
||||
:host => @host,
|
||||
:path => "#{@path}/v2/#{params[:path]}"#,
|
||||
# Causes errors for some requests like tenants?limit=1
|
||||
# :query => ('ignore_awful_caching' << Time.now.to_i.to_s)
|
||||
}))
|
||||
rescue Excon::Errors::Unauthorized => error
|
||||
if error.response.body != 'Bad username or password' # token expiration
|
||||
@openstack_must_reauthenticate = true
|
||||
authenticate
|
||||
retry
|
||||
else # bad credentials
|
||||
raise error
|
||||
end
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise case error
|
||||
when Excon::Errors::NotFound
|
||||
Fog::Compute::OpenStack::NotFound.slurp(error)
|
||||
else
|
||||
error
|
||||
end
|
||||
end
|
||||
unless response.body.empty?
|
||||
response.body = MultiJson.decode(response.body)
|
||||
end
|
||||
response
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authenticate
|
||||
if !@openstack_management_url || @openstack_must_reauthenticate
|
||||
options = {
|
||||
:openstack_tenant => @openstack_tenant,
|
||||
:openstack_api_key => @openstack_api_key,
|
||||
:openstack_username => @openstack_username,
|
||||
:openstack_auth_uri => @openstack_auth_uri,
|
||||
:openstack_auth_token => @openstack_auth_token,
|
||||
:openstack_service_type => @openstack_service_type,
|
||||
:openstack_service_name => @openstack_service_name,
|
||||
:openstack_endpoint_type => @openstack_endpoint_type
|
||||
}
|
||||
|
||||
credentials = Fog::OpenStack.authenticate_v2(options, @connection_options)
|
||||
|
||||
@current_user = credentials[:user]
|
||||
@current_tenant = credentials[:tenant]
|
||||
|
||||
@openstack_must_reauthenticate = false
|
||||
@auth_token = credentials[:token]
|
||||
@openstack_management_url = credentials[:server_management_url]
|
||||
uri = URI.parse(@openstack_management_url)
|
||||
else
|
||||
@auth_token = @openstack_auth_token
|
||||
uri = URI.parse(@openstack_management_url)
|
||||
end
|
||||
|
||||
@host = uri.host
|
||||
@path = uri.path
|
||||
@path.sub!(/\/$/, '')
|
||||
@port = uri.port
|
||||
@scheme = uri.scheme
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
0
lib/fog/openstack/models/metering/meter.rb
Normal file
0
lib/fog/openstack/models/metering/meter.rb
Normal file
0
lib/fog/openstack/models/metering/meters.rb
Normal file
0
lib/fog/openstack/models/metering/meters.rb
Normal file
24
lib/fog/openstack/models/metering/resource.rb
Normal file
24
lib/fog/openstack/models/metering/resource.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
require 'fog/core/model'
|
||||
|
||||
module Fog
|
||||
module Metering
|
||||
class OpenStack
|
||||
|
||||
class Resource < Fog::Model
|
||||
|
||||
identity :resource_id
|
||||
|
||||
attribute :project_id
|
||||
attribute :user_id
|
||||
attribute :metadata
|
||||
|
||||
def initialize(attributes)
|
||||
prepare_service_value(attributes)
|
||||
super
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
25
lib/fog/openstack/models/metering/resources.rb
Normal file
25
lib/fog/openstack/models/metering/resources.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
require 'fog/core/collection'
|
||||
require 'fog/openstack/models/metering/resource'
|
||||
|
||||
module Fog
|
||||
module Metering
|
||||
class OpenStack
|
||||
|
||||
class Resources < Fog::Collection
|
||||
model Fog::Metering::OpenStack::Resource
|
||||
|
||||
def all(detailed=true)
|
||||
load(service.list_resources.body)
|
||||
end
|
||||
|
||||
def find_by_id(resource_id)
|
||||
resource = service.get_resource(resource_id).body
|
||||
new(resource)
|
||||
rescue Fog::Metering::OpenStack::NotFound
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -176,7 +176,7 @@ module Fog
|
|||
@openstack_must_reauthenticate = false
|
||||
@openstack_service_type = options[:openstack_service_type] || ['network']
|
||||
@openstack_service_name = options[:openstack_service_name]
|
||||
@openstack_endpoint_type = options[:openstack_endpoint_type] || 'adminURL'
|
||||
@openstack_endpoint_type = options[:openstack_endpoint_type] || 'publicURL'
|
||||
@openstack_region = options[:openstack_region]
|
||||
|
||||
@connection_options = options[:connection_options] || {}
|
||||
|
|
32
lib/fog/openstack/requests/metering/get_resource.rb
Normal file
32
lib/fog/openstack/requests/metering/get_resource.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
module Fog
|
||||
module Metering
|
||||
class OpenStack
|
||||
class Real
|
||||
|
||||
def get_resource(resource_id)
|
||||
request(
|
||||
:expects => 200,
|
||||
:method => 'GET',
|
||||
:path => "resources/#{resource_id}"
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Mock
|
||||
|
||||
def get_resource(resource_id)
|
||||
response = Excon::Response.new
|
||||
response.status = 200
|
||||
response.body = {
|
||||
'resource_id'=>'glance',
|
||||
'project_id'=>'d646b40dea6347dfb8caee2da1484c56',
|
||||
'user_id'=>'1d5fd9eda19142289a60ed9330b5d284',
|
||||
'metadata'=>{}}
|
||||
response
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
55
lib/fog/openstack/requests/metering/get_samples.rb
Normal file
55
lib/fog/openstack/requests/metering/get_samples.rb
Normal file
|
@ -0,0 +1,55 @@
|
|||
module Fog
|
||||
module Metering
|
||||
class OpenStack
|
||||
class Real
|
||||
|
||||
def get_samples(meter_id, options=[])
|
||||
|
||||
data = {
|
||||
'q' => Array.new
|
||||
}
|
||||
|
||||
options.each do |opt|
|
||||
filter = {}
|
||||
|
||||
['field', 'op', 'value'].each do |key|
|
||||
filter[key] = opt[key] if opt[key]
|
||||
end
|
||||
|
||||
data['q'] << filter unless filter.empty?
|
||||
end
|
||||
|
||||
request(
|
||||
:body => Fog::JSON.encode(data),
|
||||
:expects => 200,
|
||||
:method => 'GET',
|
||||
:path => "meters/#{meter_id}"
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Mock
|
||||
|
||||
def get_samples(meter_id)
|
||||
response = Excon::Response.new
|
||||
response.status = 200
|
||||
response.body = [{
|
||||
'counter_name'=>'image.size',
|
||||
'user_id'=>'1d5fd9eda19142289a60ed9330b5d284',
|
||||
'resource_id'=>'glance',
|
||||
'timestamp'=>'2013-04-03T23:44:21',
|
||||
'resource_metadata'=>{},
|
||||
'source'=>'artificial',
|
||||
'counter_unit'=>'bytes',
|
||||
'counter_volume'=>10.0,
|
||||
'project_id'=>'d646b40dea6347dfb8caee2da1484c56',
|
||||
'message_id'=>'14e4a902-9cf3-11e2-a054-003048f5eafc',
|
||||
'counter_type'=>'gauge'}]
|
||||
response
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
56
lib/fog/openstack/requests/metering/get_statistics.rb
Normal file
56
lib/fog/openstack/requests/metering/get_statistics.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
module Fog
|
||||
module Metering
|
||||
class OpenStack
|
||||
class Real
|
||||
|
||||
def get_statistics(meter_id, options={})
|
||||
|
||||
data = {
|
||||
'period' => options['period'],
|
||||
'q' => Array.new
|
||||
}
|
||||
|
||||
options['q'].each do |opt|
|
||||
filter = {}
|
||||
|
||||
['field', 'op', 'value'].each do |key|
|
||||
filter[key] = opt[key] if opt[key]
|
||||
end
|
||||
|
||||
data['q'] << filter unless filter.empty?
|
||||
end if options['q'].is_a? Array
|
||||
|
||||
request(
|
||||
:body => Fog::JSON.encode(data),
|
||||
:expects => 200,
|
||||
:method => 'GET',
|
||||
:path => "meters/#{meter_id}/statistics"
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Mock
|
||||
|
||||
def get_statistics(meter_id, options={})
|
||||
response = Excon::Response.new
|
||||
response.status = 200
|
||||
response.body = [{
|
||||
'count'=>143,
|
||||
'duration_start'=>'2013-04-03T23:44:21',
|
||||
'min'=>10.0,
|
||||
'max'=>10.0,
|
||||
'duration_end'=>'2013-04-04T23:24:21',
|
||||
'period'=>0,
|
||||
'period_end'=>'2013-04-04T23:24:21',
|
||||
'duration'=>85200.0,
|
||||
'period_start'=>'2013-04-03T23:44:21',
|
||||
'avg'=>10.0,
|
||||
'sum'=>1430.0}]
|
||||
response
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
50
lib/fog/openstack/requests/metering/list_meters.rb
Normal file
50
lib/fog/openstack/requests/metering/list_meters.rb
Normal file
|
@ -0,0 +1,50 @@
|
|||
module Fog
|
||||
module Metering
|
||||
class OpenStack
|
||||
class Real
|
||||
|
||||
def list_meters(options=[])
|
||||
|
||||
data = {
|
||||
'q' => Array.new
|
||||
}
|
||||
|
||||
options.each do |opt|
|
||||
filter = {}
|
||||
|
||||
['field', 'op', 'value'].each do |key|
|
||||
filter[key] = opt[key] if opt[key]
|
||||
end
|
||||
|
||||
data['q'] << filter unless filter.empty?
|
||||
end
|
||||
|
||||
request(
|
||||
:body => Fog::JSON.encode(data),
|
||||
:expects => 200,
|
||||
:method => 'GET',
|
||||
:path => 'meters'
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Mock
|
||||
|
||||
def list_meters(options={})
|
||||
response = Excon::Response.new
|
||||
response.status = 200
|
||||
response.body = [{
|
||||
'user_id'=>'1d5fd9eda19142289a60ed9330b5d284',
|
||||
'name'=>'image.size',
|
||||
'resource_id'=>'glance',
|
||||
'project_id'=>'d646b40dea6347dfb8caee2da1484c56',
|
||||
'type'=>'gauge',
|
||||
'unit'=>'bytes'}]
|
||||
response
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
32
lib/fog/openstack/requests/metering/list_resources.rb
Normal file
32
lib/fog/openstack/requests/metering/list_resources.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
module Fog
|
||||
module Metering
|
||||
class OpenStack
|
||||
class Real
|
||||
|
||||
def list_resources(options = {})
|
||||
request(
|
||||
:expects => 200,
|
||||
:method => 'GET',
|
||||
:path => 'resources'
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Mock
|
||||
|
||||
def list_resources(options = {})
|
||||
response = Excon::Response.new
|
||||
response.status = 200
|
||||
response.body = [{
|
||||
'resource_id'=>'glance',
|
||||
'project_id'=>'d646b40dea6347dfb8caee2da1484c56',
|
||||
'user_id'=>'1d5fd9eda19142289a60ed9330b5d284',
|
||||
'metadata'=>{}}]
|
||||
response
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -28,7 +28,7 @@ module Fog
|
|||
unless error.response.body.empty?
|
||||
begin
|
||||
data = Fog::JSON.decode(error.response.body)
|
||||
message = data.values.first ? data.values.first['message'] : data['message']
|
||||
message = extract_message(data)
|
||||
rescue => e
|
||||
Fog::Logger.warning("Received exception '#{e}' while decoding>> #{error.response.body}")
|
||||
message = error.response.body
|
||||
|
@ -42,6 +42,16 @@ module Fog
|
|||
new_error.instance_variable_set(:@status_code, status_code)
|
||||
new_error
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.extract_message(data)
|
||||
if data.is_a?(Hash)
|
||||
message = data.values.first['message'] if data.values.first.is_a?(Hash)
|
||||
message ||= data['message']
|
||||
end
|
||||
message || data.inspect
|
||||
end
|
||||
end
|
||||
|
||||
class InternalServerError < ServiceError; end
|
||||
|
@ -96,6 +106,11 @@ module Fog
|
|||
end
|
||||
end
|
||||
|
||||
def self.json_response?(response)
|
||||
return false unless response && response.headers
|
||||
response.headers['Content-Type'] =~ %r{application/json}i ? true : false
|
||||
end
|
||||
|
||||
def self.normalize_url(endpoint)
|
||||
return nil unless endpoint
|
||||
str = endpoint.chomp " "
|
||||
|
|
|
@ -87,40 +87,25 @@ module Fog
|
|||
@connection = Fog::Connection.new(endpoint_uri.to_s, @persistent, @connection_options)
|
||||
end
|
||||
|
||||
def request(params)
|
||||
begin
|
||||
response = @connection.request(params.merge!({
|
||||
:headers => {
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => 'application/json',
|
||||
'X-Auth-Token' => auth_token
|
||||
}.merge!(params[:headers] || {}),
|
||||
:host => endpoint_uri.host,
|
||||
:path => "#{endpoint_uri.path}/#{params[:path]}"
|
||||
}))
|
||||
rescue Excon::Errors::NotFound => error
|
||||
raise NotFound.slurp(error, region)
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise BadRequest.slurp error
|
||||
rescue Excon::Errors::InternalServerError => error
|
||||
raise InternalServerError.slurp error
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise ServiceError.slurp error
|
||||
end
|
||||
unless response.body.empty?
|
||||
response.body = Fog::JSON.decode(response.body)
|
||||
end
|
||||
response
|
||||
def request(params, parse_json = true, &block)
|
||||
super(params, parse_json, &block)
|
||||
rescue Excon::Errors::NotFound => error
|
||||
raise NotFound.slurp(error, region)
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise BadRequest.slurp error
|
||||
rescue Excon::Errors::InternalServerError => error
|
||||
raise InternalServerError.slurp error
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise ServiceError.slurp error
|
||||
end
|
||||
|
||||
def authenticate
|
||||
options = {
|
||||
def authenticate(options={})
|
||||
super({
|
||||
:rackspace_api_key => @rackspace_api_key,
|
||||
:rackspace_username => @rackspace_username,
|
||||
:rackspace_auth_url => @rackspace_auth_url,
|
||||
:connection_options => @connection_options
|
||||
}
|
||||
super(options)
|
||||
})
|
||||
end
|
||||
|
||||
def service_name
|
||||
|
|
|
@ -152,30 +152,16 @@ module Fog
|
|||
true
|
||||
end
|
||||
|
||||
def request(params, parse_json = true)
|
||||
begin
|
||||
response = @connection.request(params.merge!({
|
||||
:headers => {
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => 'application/json',
|
||||
'X-Auth-Token' => auth_token
|
||||
}.merge!(params[:headers] || {}),
|
||||
:host => endpoint_uri.host,
|
||||
:path => "#{endpoint_uri.path}/#{params[:path]}",
|
||||
}))
|
||||
rescue Excon::Errors::NotFound => error
|
||||
raise Fog::Storage::Rackspace::NotFound.slurp(error, region)
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise Fog::Storage::Rackspace::BadRequest.slurp error
|
||||
rescue Excon::Errors::InternalServerError => error
|
||||
raise Fog::Storage::Rackspace::InternalServerError.slurp error
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise Fog::Storage::Rackspace::ServiceError.slurp error
|
||||
end
|
||||
if !response.body.empty? && parse_json && response.headers['Content-Type'] =~ %r{application/json}
|
||||
response.body = Fog::JSON.decode(response.body)
|
||||
end
|
||||
response
|
||||
def request(params, parse_json = true, &block)
|
||||
super(params, parse_json, &block)
|
||||
rescue Excon::Errors::NotFound => error
|
||||
raise Fog::Storage::Rackspace::NotFound.slurp(error, region)
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise Fog::Storage::Rackspace::BadRequest.slurp error
|
||||
rescue Excon::Errors::InternalServerError => error
|
||||
raise Fog::Storage::Rackspace::InternalServerError.slurp error
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise Fog::Storage::Rackspace::ServiceError.slurp error
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -190,7 +190,6 @@ module Fog
|
|||
@rackspace_servicenet = options[:rackspace_servicenet]
|
||||
@rackspace_auth_token = options[:rackspace_auth_token]
|
||||
@rackspace_endpoint = Fog::Rackspace.normalize_url(options[:rackspace_compute_v1_url] || options[:rackspace_management_url])
|
||||
@rackspace_must_reauthenticate = false
|
||||
@connection_options = options[:connection_options] || {}
|
||||
authenticate
|
||||
Excon.defaults[:ssl_verify_peer] = false if service_net?
|
||||
|
@ -202,56 +201,29 @@ module Fog
|
|||
@connection.reset
|
||||
end
|
||||
|
||||
def request(params)
|
||||
begin
|
||||
response = @connection.request(params.merge({
|
||||
:headers => {
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Auth-Token' => auth_token
|
||||
}.merge!(params[:headers] || {}),
|
||||
:host => endpoint_uri.host,
|
||||
:path => "#{endpoint_uri.path}/#{params[:path]}",
|
||||
}))
|
||||
rescue Excon::Errors::Unauthorized => error
|
||||
if error.response.body != 'Bad username or password' # token expiration
|
||||
@rackspace_must_reauthenticate = true
|
||||
authenticate
|
||||
retry
|
||||
else # bad credentials
|
||||
raise error
|
||||
end
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise case error
|
||||
when Excon::Errors::NotFound
|
||||
NotFound.slurp(error, region)
|
||||
else
|
||||
error
|
||||
end
|
||||
end
|
||||
unless response.body.empty?
|
||||
response.body = Fog::JSON.decode(response.body)
|
||||
end
|
||||
response
|
||||
def request(params, parse_json = true, &block)
|
||||
super(params, parse_json, &block)
|
||||
rescue Excon::Errors::NotFound => error
|
||||
raise NotFound.slurp(error, region)
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise BadRequest.slurp error
|
||||
rescue Excon::Errors::InternalServerError => error
|
||||
raise InternalServerError.slurp error
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise ServiceError.slurp error
|
||||
end
|
||||
|
||||
|
||||
def service_net?
|
||||
@rackspace_servicenet == true
|
||||
end
|
||||
|
||||
def authenticate
|
||||
if @rackspace_must_reauthenticate || @rackspace_auth_token.nil?
|
||||
options = {
|
||||
:rackspace_api_key => @rackspace_api_key,
|
||||
:rackspace_username => @rackspace_username,
|
||||
:rackspace_auth_url => @rackspace_auth_url,
|
||||
:connection_options => @connection_options
|
||||
}
|
||||
super(options)
|
||||
else
|
||||
@auth_token = @rackspace_auth_token
|
||||
@uri = URI.parse(@rackspace_endpoint)
|
||||
end
|
||||
def authenticate(options={})
|
||||
super({
|
||||
:rackspace_api_key => @rackspace_api_key,
|
||||
:rackspace_username => @rackspace_username,
|
||||
:rackspace_auth_url => @rackspace_auth_url,
|
||||
:connection_options => @connection_options
|
||||
})
|
||||
end
|
||||
|
||||
def service_name
|
||||
|
|
|
@ -139,45 +139,25 @@ module Fog
|
|||
@connection = Fog::Connection.new(endpoint_uri.to_s, @persistent, @connection_options)
|
||||
end
|
||||
|
||||
def request(params)
|
||||
begin
|
||||
response = @connection.request(params.merge!({
|
||||
:headers => {
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => 'application/json',
|
||||
'X-Auth-Token' => auth_token
|
||||
}.merge!(params[:headers] || {}),
|
||||
:host => endpoint_uri.host,
|
||||
:path => "#{endpoint_uri.path}/#{params[:path]}"
|
||||
}))
|
||||
rescue Excon::Errors::NotFound => error
|
||||
raise NotFound.slurp(error, region)
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise BadRequest.slurp error
|
||||
rescue Excon::Errors::InternalServerError => error
|
||||
raise InternalServerError.slurp error
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise ServiceError.slurp error
|
||||
end
|
||||
|
||||
unless response.body.empty?
|
||||
begin
|
||||
response.body = Fog::JSON.decode(response.body)
|
||||
rescue MultiJson::DecodeError => e
|
||||
response.body = {}
|
||||
end
|
||||
end
|
||||
response
|
||||
def request(params, parse_json = true, &block)
|
||||
super(params, parse_json, &block)
|
||||
rescue Excon::Errors::NotFound => error
|
||||
raise NotFound.slurp(error, region)
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise BadRequest.slurp error
|
||||
rescue Excon::Errors::InternalServerError => error
|
||||
raise InternalServerError.slurp error
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise ServiceError.slurp error
|
||||
end
|
||||
|
||||
def authenticate
|
||||
options = {
|
||||
def authenticate(options={})
|
||||
super({
|
||||
:rackspace_api_key => @rackspace_api_key,
|
||||
:rackspace_username => @rackspace_username,
|
||||
:rackspace_auth_url => @rackspace_auth_url,
|
||||
:connection_options => @connection_options
|
||||
}
|
||||
super(options)
|
||||
})
|
||||
end
|
||||
|
||||
def service_name
|
||||
|
|
|
@ -85,45 +85,29 @@ module Fog
|
|||
@connection = Fog::Connection.new(endpoint_uri.to_s, @persistent, @connection_options)
|
||||
end
|
||||
|
||||
def request(params)
|
||||
begin
|
||||
response = @connection.request(params.merge!({
|
||||
:headers => {
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => 'application/json',
|
||||
'X-Auth-Token' => auth_token
|
||||
}.merge!(params[:headers] || {}),
|
||||
:host => endpoint_uri.host,
|
||||
:path => "#{endpoint_uri.path}/#{params[:path]}"
|
||||
}))
|
||||
rescue Excon::Errors::NotFound => error
|
||||
raise NotFound.slurp(error, region)
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise BadRequest.slurp error
|
||||
rescue Excon::Errors::InternalServerError => error
|
||||
raise InternalServerError.slurp error
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise ServiceError.slurp error
|
||||
end
|
||||
unless response.body.empty?
|
||||
response.body = Fog::JSON.decode(response.body)
|
||||
end
|
||||
response
|
||||
def request(params, parse_json = true, &block)
|
||||
super(params, parse_json, &block)
|
||||
rescue Excon::Errors::NotFound => error
|
||||
raise NotFound.slurp(error, region)
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise BadRequest.slurp error
|
||||
rescue Excon::Errors::InternalServerError => error
|
||||
raise InternalServerError.slurp error
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise ServiceError.slurp error
|
||||
end
|
||||
|
||||
def endpoint_uri(service_endpoint_url=nil)
|
||||
@uri = super(@rackspace_endpoint || service_endpoint_url, :rackspace_database_url)
|
||||
end
|
||||
|
||||
def authenticate
|
||||
options = {
|
||||
def authenticate(options={})
|
||||
super({
|
||||
:rackspace_api_key => @rackspace_api_key,
|
||||
:rackspace_username => @rackspace_username,
|
||||
:rackspace_auth_url => @rackspace_auth_url,
|
||||
:connection_options => @connection_options
|
||||
}
|
||||
|
||||
super(options)
|
||||
})
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -6,6 +6,12 @@ module Fog
|
|||
class Rackspace < Fog::Service
|
||||
include Fog::Rackspace::Errors
|
||||
|
||||
class ServiceError < Fog::Rackspace::Errors::ServiceError; end
|
||||
class InternalServerError < Fog::Rackspace::Errors::InternalServerError; end
|
||||
class BadRequest < Fog::Rackspace::Errors::BadRequest; end
|
||||
class Conflict < Fog::Rackspace::Errors::Conflict; end
|
||||
class ServiceUnavailable < Fog::Rackspace::Errors::ServiceUnavailable; end
|
||||
|
||||
class CallbackError < Fog::Errors::Error
|
||||
attr_reader :response, :message, :details
|
||||
def initialize(response)
|
||||
|
@ -95,9 +101,6 @@ module Fog
|
|||
|
||||
deprecation_warnings(options)
|
||||
|
||||
@connection_options[:headers] ||= {}
|
||||
@connection_options[:headers].merge!({ 'Content-Type' => 'application/json', 'X-Auth-Token' => auth_token })
|
||||
|
||||
@persistent = options[:persistent] || false
|
||||
@connection = Fog::Connection.new(endpoint_uri.to_s, @persistent, @connection_options)
|
||||
end
|
||||
|
@ -108,25 +111,22 @@ module Fog
|
|||
|
||||
private
|
||||
|
||||
def request(params)
|
||||
#TODO - Unify code with other rackspace services
|
||||
def request(params, parse_json = true, &block)
|
||||
begin
|
||||
response = @connection.request(params.merge!({
|
||||
:path => "#{endpoint_uri.path}/#{params[:path]}"
|
||||
}))
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise Fog::Rackspace::Errors::BadRequest.slurp error
|
||||
rescue Excon::Errors::Conflict => error
|
||||
raise Fog::Rackspace::Errors::Conflict.slurp error
|
||||
super(params, parse_json, &block)
|
||||
rescue Excon::Errors::NotFound => error
|
||||
raise NotFound.slurp(error, region)
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise BadRequest.slurp error
|
||||
rescue Excon::Errors::InternalServerError => error
|
||||
raise InternalServerError.slurp error
|
||||
rescue Excon::Errors::ServiceUnavailable => error
|
||||
raise Fog::Rackspace::Errors::ServiceUnavailable.slurp error
|
||||
raise ServiceUnavailable.slurp error
|
||||
rescue Excon::Errors::Conflict => error
|
||||
raise Conflict.slurp error
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise ServiceError.slurp error
|
||||
end
|
||||
unless response.body.empty?
|
||||
response.body = Fog::JSON.decode(response.body)
|
||||
end
|
||||
response
|
||||
end
|
||||
|
||||
def array_to_query_string(arr)
|
||||
|
@ -164,15 +164,13 @@ module Fog
|
|||
@auth_token = credentials['X-Auth-Token']
|
||||
end
|
||||
|
||||
def authenticate
|
||||
options = {
|
||||
def authenticate(options={})
|
||||
super({
|
||||
:rackspace_api_key => @rackspace_api_key,
|
||||
:rackspace_username => @rackspace_username,
|
||||
:rackspace_auth_url => @rackspace_auth_url,
|
||||
:connection_options => @connection_options
|
||||
}
|
||||
|
||||
super(options)
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,7 @@ require 'fog/rackspace'
|
|||
module Fog
|
||||
module Rackspace
|
||||
class Identity < Fog::Service
|
||||
|
||||
US_ENDPOINT = 'https://identity.api.rackspacecloud.com/v2.0'
|
||||
UK_ENDPOINT = 'https://lon.identity.api.rackspacecloud.com/v2.0'
|
||||
|
||||
|
@ -33,7 +34,7 @@ module Fog
|
|||
request :update_user
|
||||
request :delete_user
|
||||
|
||||
class Mock
|
||||
class Mock < Fog::Rackspace::Service
|
||||
attr_reader :service_catalog
|
||||
|
||||
def request
|
||||
|
@ -41,7 +42,7 @@ module Fog
|
|||
end
|
||||
end
|
||||
|
||||
class Real
|
||||
class Real < Fog::Rackspace::Service
|
||||
attr_reader :service_catalog, :auth_token
|
||||
|
||||
def initialize(options={})
|
||||
|
@ -50,36 +51,19 @@ module Fog
|
|||
@rackspace_region = options[:rackspace_region]
|
||||
@rackspace_auth_url = options[:rackspace_auth_url] || US_ENDPOINT
|
||||
|
||||
uri = URI.parse(@rackspace_auth_url)
|
||||
@host = uri.host
|
||||
@path = uri.path
|
||||
@port = uri.port
|
||||
@scheme = uri.scheme
|
||||
@uri = URI.parse(@rackspace_auth_url)
|
||||
@host = @uri.host
|
||||
@path = @uri.path
|
||||
@port = @uri.port
|
||||
@scheme = @uri.scheme
|
||||
@persistent = options[:persistent] || false
|
||||
@connection_options = options[:connection_options] || {}
|
||||
@connection = Fog::Connection.new(uri.to_s, @persistent, @connection_options)
|
||||
@connection = Fog::Connection.new(@uri.to_s, @persistent, @connection_options)
|
||||
|
||||
authenticate
|
||||
end
|
||||
|
||||
def request(params)
|
||||
begin
|
||||
parameters = params.merge!({
|
||||
:headers => {
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => 'application/json',
|
||||
'X-Auth-Token' => @auth_token
|
||||
},
|
||||
:host => @host,
|
||||
:path => "#{@path}/#{params[:path]}"
|
||||
})
|
||||
response = @connection.request(parameters)
|
||||
response.body = Fog::JSON.decode(response.body) unless response.body.empty?
|
||||
response
|
||||
end
|
||||
end
|
||||
|
||||
def authenticate
|
||||
def authenticate(options={})
|
||||
data = self.create_token(@rackspace_username, @rackspace_api_key).body
|
||||
@service_catalog = ServiceCatalog.from_response(self, data)
|
||||
@auth_token = data['access']['token']['id']
|
||||
|
|
|
@ -119,41 +119,25 @@ module Fog
|
|||
@connection = Fog::Connection.new(endpoint_uri.to_s, @persistent, @connection_options)
|
||||
end
|
||||
|
||||
def request(params)
|
||||
#TODO - Unify code with other rackspace services
|
||||
begin
|
||||
response = @connection.request(params.merge!({
|
||||
:headers => {
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => 'application/json',
|
||||
'X-Auth-Token' => auth_token
|
||||
}.merge!(params[:headers] || {}),
|
||||
:host => endpoint_uri.host,
|
||||
:path => "#{endpoint_uri.path}/#{params[:path]}"
|
||||
}))
|
||||
rescue Excon::Errors::NotFound => error
|
||||
raise NotFound.slurp(error, region)
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise BadRequest.slurp error
|
||||
rescue Excon::Errors::InternalServerError => error
|
||||
raise InternalServerError.slurp error
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise ServiceError.slurp error
|
||||
end
|
||||
unless response.body.empty?
|
||||
response.body = Fog::JSON.decode(response.body)
|
||||
end
|
||||
response
|
||||
def request(params, parse_json = true, &block)
|
||||
super(params, parse_json, &block)
|
||||
rescue Excon::Errors::NotFound => error
|
||||
raise NotFound.slurp(error, region)
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise BadRequest.slurp error
|
||||
rescue Excon::Errors::InternalServerError => error
|
||||
raise InternalServerError.slurp error
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise ServiceError.slurp error
|
||||
end
|
||||
|
||||
def authenticate
|
||||
options = {
|
||||
def authenticate(options={})
|
||||
super({
|
||||
:rackspace_api_key => @rackspace_api_key,
|
||||
:rackspace_username => @rackspace_username,
|
||||
:rackspace_auth_url => @rackspace_auth_url,
|
||||
:connection_options => @connection_options
|
||||
}
|
||||
super(options)
|
||||
})
|
||||
end
|
||||
|
||||
def service_name
|
||||
|
|
|
@ -18,7 +18,7 @@ module Fog
|
|||
new(data)
|
||||
rescue Excon::Errors::NotFound
|
||||
nil
|
||||
rescue Excon::Errors::NotAuthorized
|
||||
rescue Excon::Errors::Unauthorized
|
||||
nil
|
||||
end
|
||||
|
||||
|
@ -27,7 +27,7 @@ module Fog
|
|||
new(data)
|
||||
rescue Excon::Errors::NotFound
|
||||
nil
|
||||
rescue Excon::Errors::NotAuthorized
|
||||
rescue Excon::Errors::Unauthorized
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
module Fog
|
||||
module Storage
|
||||
class Rackspace
|
||||
class Real
|
||||
|
||||
# Deletes multiple objects or containers with a single request.
|
||||
#
|
||||
# To delete objects from a single container, +container+ may be provided
|
||||
# and +object_names+ should be an Array of object names within the container.
|
||||
#
|
||||
# To delete objects from multiple containers or delete containers,
|
||||
# +container+ should be +nil+ and all +object_names+ should be prefixed with a container name.
|
||||
#
|
||||
# Containers must be empty when deleted. +object_names+ are processed in the order given,
|
||||
# so objects within a container should be listed first to empty the container.
|
||||
#
|
||||
# Up to 10,000 objects may be deleted in a single request.
|
||||
# The server will respond with +200 OK+ for all requests.
|
||||
# +response.body+ must be inspected for actual results.
|
||||
#
|
||||
# @example Delete objects from a container
|
||||
# object_names = ['object', 'another/object']
|
||||
# conn.delete_multiple_objects('my_container', object_names)
|
||||
#
|
||||
# @example Delete objects from multiple containers
|
||||
# object_names = ['container_a/object', 'container_b/object']
|
||||
# conn.delete_multiple_objects(nil, object_names)
|
||||
#
|
||||
# @example Delete a container and all it's objects
|
||||
# object_names = ['my_container/object_a', 'my_container/object_b', 'my_container']
|
||||
# conn.delete_multiple_objects(nil, object_names)
|
||||
#
|
||||
# @param container [String,nil] Name of container.
|
||||
# @param object_names [Array<String>] Object names to be deleted.
|
||||
# @param options [Hash] Additional request headers.
|
||||
#
|
||||
# @return [Excon::Response]
|
||||
# * body [Hash] - Results of the operation.
|
||||
# * "Number Not Found" [Integer] - Number of missing objects or containers.
|
||||
# * "Response Status" [String] - Response code for the subrequest of the last failed operation.
|
||||
# * "Errors" [Array<object_name, response_status>]
|
||||
# * object_name [String] - Object that generated an error when the delete was attempted.
|
||||
# * response_status [String] - Response status from the subrequest for object_name.
|
||||
# * "Number Deleted" [Integer] - Number of objects or containers deleted.
|
||||
# * "Response Body" [String] - Response body for "Response Status".
|
||||
#
|
||||
# @raise [Fog::Storage::Rackspace::NotFound] HTTP 404
|
||||
# @raise [Fog::Storage::Rackspace::BadRequest] HTTP 400
|
||||
# @raise [Fog::Storage::Rackspace::InternalServerError] HTTP 500
|
||||
# @raise [Fog::Storage::Rackspace::ServiceError]
|
||||
# @raise [Excon::Errors::Unauthorized] HTTP 401
|
||||
#
|
||||
# @see http://docs.rackspace.com/files/api/v1/cf-devguide/content/Bulk_Delete-d1e2338.html
|
||||
def delete_multiple_objects(container, object_names, options = {})
|
||||
body = object_names.map do |name|
|
||||
object_name = container ? "#{ container }/#{ name }" : name
|
||||
URI.encode(object_name)
|
||||
end.join("\n")
|
||||
|
||||
response = request({
|
||||
:expects => 200,
|
||||
:method => 'DELETE',
|
||||
:headers => options.merge('Content-Type' => 'text/plain',
|
||||
'Accept' => 'application/json'),
|
||||
:body => body,
|
||||
:query => { 'bulk-delete' => true }
|
||||
}, false)
|
||||
response.body = Fog::JSON.decode(response.body)
|
||||
response
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,50 @@
|
|||
module Fog
|
||||
module Storage
|
||||
class Rackspace
|
||||
class Real
|
||||
|
||||
# Delete a static large object.
|
||||
#
|
||||
# Deletes the SLO manifest +object+ and all segments that it references.
|
||||
# The server will respond with +200 OK+ for all requests.
|
||||
# +response.body+ must be inspected for actual results.
|
||||
#
|
||||
# @param container [String] Name of container.
|
||||
# @param object [String] Name of the SLO manifest object.
|
||||
# @param options [Hash] Additional request headers.
|
||||
#
|
||||
# @return [Excon::Response]
|
||||
# * body [Hash] - Results of the operation.
|
||||
# * "Number Not Found" [Integer] - Number of missing segments.
|
||||
# * "Response Status" [String] - Response code for the subrequest of the last failed operation.
|
||||
# * "Errors" [Array<object_name, response_status>]
|
||||
# * object_name [String] - Object that generated an error when the delete was attempted.
|
||||
# * response_status [String] - Response status from the subrequest for object_name.
|
||||
# * "Number Deleted" [Integer] - Number of segments deleted.
|
||||
# * "Response Body" [String] - Response body for Response Status.
|
||||
#
|
||||
# @raise [Fog::Storage::Rackspace::NotFound] HTTP 404
|
||||
# @raise [Fog::Storage::Rackspace::BadRequest] HTTP 400
|
||||
# @raise [Fog::Storage::Rackspace::InternalServerError] HTTP 500
|
||||
# @raise [Fog::Storage::Rackspace::ServiceError]
|
||||
# @raise [Excon::Errors::Unauthorized] HTTP 401
|
||||
#
|
||||
# @see http://docs.rackspace.com/files/api/v1/cf-devguide/content/Deleting_a_Large_Object-d1e2228.html
|
||||
def delete_static_large_object(container, object, options = {})
|
||||
response = request({
|
||||
:expects => 200,
|
||||
:method => 'DELETE',
|
||||
:headers => options.merge('Content-Type' => 'text/plain',
|
||||
'Accept' => 'application/json'),
|
||||
:path => "#{Fog::Rackspace.escape(container)}/#{Fog::Rackspace.escape(object)}",
|
||||
:query => { 'multipart-manifest' => 'delete' }
|
||||
}, false)
|
||||
response.body = Fog::JSON.decode(response.body)
|
||||
response
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -23,7 +23,9 @@ module Fog
|
|||
:expects => 200,
|
||||
:method => 'GET',
|
||||
:path => "#{Fog::Rackspace.escape(container)}/#{Fog::Rackspace.escape(object)}"
|
||||
}), false)
|
||||
}),
|
||||
false,
|
||||
&block)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
module Fog
|
||||
module Storage
|
||||
class Rackspace
|
||||
class Real
|
||||
|
||||
# Create a new dynamic large object manifest
|
||||
#
|
||||
# Creates an object with a +X-Object-Manifest+ header that specifies the common prefix ("<container>/<prefix>")
|
||||
# for all uploaded segments. Retrieving the manifest object streams all segments matching this prefix.
|
||||
# Segments must sort in the order they should be concatenated. Note that any future objects stored in the container
|
||||
# along with the segments that match the prefix will be included when retrieving the manifest object.
|
||||
#
|
||||
# All segments must be stored in the same container, but may be in a different container than the manifest object.
|
||||
# The default +X-Object-Manifest+ header is set to "+container+/+object+", but may be overridden in +options+
|
||||
# to specify the prefix and/or the container where segments were stored.
|
||||
# If overridden, names should be CGI escaped (excluding spaces) if needed (see {Fog::Rackspace.escape}).
|
||||
#
|
||||
# @param container [String] Name for container where +object+ will be stored. Should be < 256 bytes and must not contain '/'
|
||||
# @param object [String] Name for manifest object.
|
||||
# @param options [Hash] Config headers for +object+.
|
||||
# @option options [String] 'X-Object-Manifest' ("container/object") "<container>/<prefix>" for segment objects.
|
||||
#
|
||||
# @raise [Fog::Storage::Rackspace::NotFound] HTTP 404
|
||||
# @raise [Fog::Storage::Rackspace::BadRequest] HTTP 400
|
||||
# @raise [Fog::Storage::Rackspace::InternalServerError] HTTP 500
|
||||
# @raise [Fog::Storage::Rackspace::ServiceError]
|
||||
# @raise [Excon::Errors::Unauthorized]
|
||||
#
|
||||
# @see http://docs.rackspace.com/files/api/v1/cf-devguide/content/Large_Object_Creation-d1e2019.html
|
||||
def put_dynamic_obj_manifest(container, object, options = {})
|
||||
path = "#{Fog::Rackspace.escape(container)}/#{Fog::Rackspace.escape(object)}"
|
||||
headers = {'X-Object-Manifest' => path}.merge(options)
|
||||
request(
|
||||
:expects => 201,
|
||||
:headers => headers,
|
||||
:method => 'PUT',
|
||||
:path => path
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,38 +3,11 @@ module Fog
|
|||
class Rackspace
|
||||
class Real
|
||||
|
||||
# Create a new manifest object
|
||||
# Create a new dynamic large object manifest
|
||||
#
|
||||
# Creates an object with a +X-Object-Manifest+ header that specifies the common prefix ("<container>/<prefix>")
|
||||
# for all uploaded segments. Retrieving the manifest object streams all segments matching this prefix.
|
||||
# Segments must sort in the order they should be concatenated. Note that any future objects stored in the container
|
||||
# along with the segments that match the prefix will be included when retrieving the manifest object.
|
||||
#
|
||||
# All segments must be stored in the same container, but may be in a different container than the manifest object.
|
||||
# The default +X-Object-Manifest+ header is set to "+container+/+object+", but may be overridden in +options+
|
||||
# to specify the prefix and/or the container where segments were stored.
|
||||
# If overridden, names should be CGI escaped (excluding spaces) if needed (see {Fog::Rackspace.escape}).
|
||||
#
|
||||
# @param container [String] Name for container where +object+ will be stored. Should be < 256 bytes and must not contain '/'
|
||||
# @param object [String] Name for manifest object.
|
||||
# @param options [Hash] Config headers for +object+.
|
||||
# @option options [String] 'X-Object-Manifest' ("container/object") "<container>/<prefix>" for segment objects.
|
||||
#
|
||||
# @raise [Fog::Storage::Rackspace::NotFound] HTTP 404
|
||||
# @raise [Fog::Storage::Rackspace::BadRequest] HTTP 400
|
||||
# @raise [Fog::Storage::Rackspace::InternalServerError] HTTP 500
|
||||
# @raise [Fog::Storage::Rackspace::ServiceError]
|
||||
#
|
||||
# @see http://docs.rackspace.com/files/api/v1/cf-devguide/content/Large_Object_Creation-d1e2019.html
|
||||
# This is an alias for {#put_dynamic_obj_manifest} for backward compatibility.
|
||||
def put_object_manifest(container, object, options = {})
|
||||
path = "#{Fog::Rackspace.escape(container)}/#{Fog::Rackspace.escape(object)}"
|
||||
headers = {'X-Object-Manifest' => path}.merge(options)
|
||||
request(
|
||||
:expects => 201,
|
||||
:headers => headers,
|
||||
:method => 'PUT',
|
||||
:path => path
|
||||
)
|
||||
put_dynamic_obj_manifest(container, object, options)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
module Fog
|
||||
module Storage
|
||||
class Rackspace
|
||||
class Real
|
||||
|
||||
# Create a new static large object manifest.
|
||||
#
|
||||
# A static large object is similar to a dynamic large object. Whereas a GET for a dynamic large object manifest
|
||||
# will stream segments based on the manifest's +X-Object-Manifest+ object name prefix, a static large object
|
||||
# manifest streams segments which are defined by the user within the manifest. Information about each segment is
|
||||
# provided in +segments+ as an Array of Hash objects, ordered in the sequence which the segments should be streamed.
|
||||
#
|
||||
# When the SLO manifest is received, each segment's +etag+ and +size_bytes+ will be verified.
|
||||
# The +etag+ for each segment is returned in the response to {#put_object}, but may also be calculated.
|
||||
# e.g. +Digest::MD5.hexdigest(segment_data)+
|
||||
#
|
||||
# The maximum number of segments for a static large object is 1000, and all segments (except the last) must be
|
||||
# at least 1 MiB in size. Unlike a dynamic large object, segments are not required to be in the same container.
|
||||
#
|
||||
# @example
|
||||
# segments = [
|
||||
# { :path => 'segments_container/first_segment',
|
||||
# :etag => 'md5 for first_segment',
|
||||
# :size_bytes => 'byte size of first_segment' },
|
||||
# { :path => 'segments_container/second_segment',
|
||||
# :etag => 'md5 for second_segment',
|
||||
# :size_bytes => 'byte size of second_segment' }
|
||||
# ]
|
||||
# put_static_obj_manifest('my_container', 'my_large_object', segments)
|
||||
#
|
||||
# @param container [String] Name for container where +object+ will be stored.
|
||||
# Should be < 256 bytes and must not contain '/'
|
||||
# @param object [String] Name for manifest object.
|
||||
# @param segments [Array<Hash>] Segment data for the object.
|
||||
# @param options [Hash] Config headers for +object+.
|
||||
#
|
||||
# @raise [Fog::Storage::Rackspace::NotFound] HTTP 404
|
||||
# @raise [Fog::Storage::Rackspace::BadRequest] HTTP 400
|
||||
# @raise [Fog::Storage::Rackspace::InternalServerError] HTTP 500
|
||||
# @raise [Fog::Storage::Rackspace::ServiceError]
|
||||
# @raise [Excon::Errors::Unauthorized] HTTP 401
|
||||
#
|
||||
# @see http://docs.rackspace.com/files/api/v1/cf-devguide/content/Uploading_the_Manifext-d1e2227.html
|
||||
def put_static_obj_manifest(container, object, segments, options = {})
|
||||
request(
|
||||
:expects => 201,
|
||||
:method => 'PUT',
|
||||
:headers => options,
|
||||
:body => Fog::JSON.encode(segments),
|
||||
:path => "#{Fog::Rackspace.escape(container)}/#{Fog::Rackspace.escape(object)}",
|
||||
:query => { 'multipart-manifest' => 'put' }
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -26,12 +26,54 @@ module Fog
|
|||
@uri = URI.parse url
|
||||
end
|
||||
|
||||
def authenticate(options)
|
||||
def authenticate(options={})
|
||||
self.send authentication_method, options
|
||||
end
|
||||
|
||||
def request(params, parse_json = true, &block)
|
||||
first_attempt = true
|
||||
begin
|
||||
response = @connection.request(request_params(params), &block)
|
||||
rescue Excon::Errors::Unauthorized => error
|
||||
raise error unless first_attempt
|
||||
first_attempt = false
|
||||
authenticate
|
||||
retry
|
||||
end
|
||||
|
||||
process_response(response) if parse_json
|
||||
response
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_response(response)
|
||||
if response && response.body && response.body.is_a?(String) && Fog::Rackspace.json_response?(response)
|
||||
begin
|
||||
response.body = Fog::JSON.decode(response.body)
|
||||
rescue MultiJson::DecodeError => e
|
||||
Fog::Logger.warning("Error Parsing response json - #{e}")
|
||||
response.body = {}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def headers(options={})
|
||||
h = {
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => 'application/json',
|
||||
'X-Auth-Token' => auth_token
|
||||
}.merge(options[:headers] || {})
|
||||
end
|
||||
|
||||
def request_params(params)
|
||||
params.merge({
|
||||
:headers => headers(params),
|
||||
:host => endpoint_uri.host,
|
||||
:path => "#{endpoint_uri.path}/#{params[:path]}"
|
||||
})
|
||||
end
|
||||
|
||||
def authentication_method
|
||||
if v2_authentication?
|
||||
:authenticate_v2
|
||||
|
|
|
@ -25,6 +25,8 @@ module Fog
|
|||
request :copy_object
|
||||
request :delete_container
|
||||
request :delete_object
|
||||
request :delete_static_large_object
|
||||
request :delete_multiple_objects
|
||||
request :get_container
|
||||
request :get_containers
|
||||
request :get_object
|
||||
|
@ -35,6 +37,8 @@ module Fog
|
|||
request :put_container
|
||||
request :put_object
|
||||
request :put_object_manifest
|
||||
request :put_dynamic_obj_manifest
|
||||
request :put_static_obj_manifest
|
||||
request :post_set_meta_temp_url_key
|
||||
|
||||
module Utils
|
||||
|
@ -54,7 +58,7 @@ module Fog
|
|||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
class Mock < Fog::Rackspace::Service
|
||||
include Utils
|
||||
|
@ -107,7 +111,7 @@ module Fog
|
|||
@rackspace_servicenet = options[:rackspace_servicenet]
|
||||
@rackspace_auth_token = options[:rackspace_auth_token]
|
||||
@rackspace_storage_url = options[:rackspace_storage_url]
|
||||
@rackspace_cdn_url = options[:rackspace_cdn_url]
|
||||
@rackspace_cdn_url = options[:rackspace_cdn_url]
|
||||
@rackspace_region = options[:rackspace_region] || :dfw
|
||||
@rackspace_temp_url_key = options[:rackspace_temp_url_key]
|
||||
@rackspace_must_reauthenticate = false
|
||||
|
@ -117,8 +121,8 @@ module Fog
|
|||
@persistent = options[:persistent] || false
|
||||
Excon.defaults[:ssl_verify_peer] = false if service_net?
|
||||
@connection = Fog::Connection.new(endpoint_uri.to_s, @persistent, @connection_options)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Return Account Details
|
||||
# @return [Fog::Storage::Rackspace::Account] account details object
|
||||
def account
|
||||
|
@ -139,43 +143,21 @@ module Fog
|
|||
end
|
||||
|
||||
def request(params, parse_json = true, &block)
|
||||
begin
|
||||
response = @connection.request(params.merge({
|
||||
:headers => {
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => 'application/json',
|
||||
'X-Auth-Token' => auth_token
|
||||
}.merge!(params[:headers] || {}),
|
||||
:host => endpoint_uri.host,
|
||||
:path => "#{endpoint_uri.path}/#{params[:path]}",
|
||||
}), &block)
|
||||
rescue Excon::Errors::Unauthorized => error
|
||||
if error.response.body != 'Bad username or password' # token expiration
|
||||
@rackspace_must_reauthenticate = true
|
||||
authenticate
|
||||
retry
|
||||
else # bad credentials
|
||||
raise error
|
||||
end
|
||||
rescue Excon::Errors::NotFound => error
|
||||
raise NotFound.slurp(error, region)
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise BadRequest.slurp error
|
||||
rescue Excon::Errors::InternalServerError => error
|
||||
raise InternalServerError.slurp error
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise ServiceError.slurp error
|
||||
end
|
||||
if !response.body.empty? && parse_json && response.headers['Content-Type'] =~ %r{application/json}
|
||||
response.body = Fog::JSON.decode(response.body)
|
||||
end
|
||||
response
|
||||
super(params, parse_json, &block)
|
||||
rescue Excon::Errors::NotFound => error
|
||||
raise NotFound.slurp(error, region)
|
||||
rescue Excon::Errors::BadRequest => error
|
||||
raise BadRequest.slurp error
|
||||
rescue Excon::Errors::InternalServerError => error
|
||||
raise InternalServerError.slurp error
|
||||
rescue Excon::Errors::HTTPStatusError => error
|
||||
raise ServiceError.slurp error
|
||||
end
|
||||
|
||||
def service_net?
|
||||
@rackspace_servicenet == true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def authenticate
|
||||
if @rackspace_must_reauthenticate || @rackspace_auth_token.nil?
|
||||
options = {
|
||||
|
@ -183,14 +165,14 @@ module Fog
|
|||
:rackspace_username => @rackspace_username,
|
||||
:rackspace_auth_url => @rackspace_auth_url,
|
||||
:connection_options => @connection_options
|
||||
}
|
||||
}
|
||||
super(options)
|
||||
else
|
||||
@auth_token = @rackspace_auth_token
|
||||
@uri = URI.parse(@rackspace_storage_url)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def service_name
|
||||
:cloudFiles
|
||||
end
|
||||
|
@ -206,15 +188,15 @@ module Fog
|
|||
@uri.host = "snet-#{@uri.host}" if service_net?
|
||||
@uri
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
||||
private
|
||||
|
||||
def authenticate_v1(options)
|
||||
credentials = Fog::Rackspace.authenticate(options, @connection_options)
|
||||
endpoint_uri credentials['X-Storage-Url']
|
||||
@auth_token = credentials['X-Auth-Token']
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -64,7 +64,8 @@ module Fog
|
|||
:aws_secret_access_key => @riakcs_secret_access_key,
|
||||
:host => @host,
|
||||
:port => @port,
|
||||
:scheme => @scheme
|
||||
:scheme => @scheme,
|
||||
:connection_options => @connection_options
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -56,7 +56,8 @@ module Fog
|
|||
:aws_secret_access_key => @riakcs_secret_access_key,
|
||||
:host => @host,
|
||||
:port => @port,
|
||||
:scheme => @scheme
|
||||
:scheme => @scheme,
|
||||
:connection_options => @connection_options
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -105,6 +105,19 @@ class AWS
|
|||
|
||||
})
|
||||
|
||||
DB_LOG_FILE = {
|
||||
'LastWritten' => Time,
|
||||
'Size' => Integer,
|
||||
'LogFileName' => String
|
||||
}
|
||||
|
||||
DESCRIBE_DB_LOG_FILES = BASIC.merge({
|
||||
'DescribeDBLogFilesResult' => {
|
||||
'Marker' => Fog::Nullable::String,
|
||||
'DBLogFiles' => [DB_LOG_FILE]
|
||||
}
|
||||
})
|
||||
|
||||
SNAPSHOT={
|
||||
'AllocatedStorage' => Integer,
|
||||
'AvailabilityZone' => String,
|
||||
|
|
19
tests/aws/requests/rds/log_file_tests.rb
Normal file
19
tests/aws/requests/rds/log_file_tests.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
Shindo.tests('AWS::RDS | log file requests', %w[aws rds]) do
|
||||
tests('success') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
suffix = rand(65536).to_s(16)
|
||||
@db_instance_id = "fog-test-#{suffix}"
|
||||
|
||||
tests('#describe_db_log_files').formats(AWS::RDS::Formats::DESCRIBE_DB_LOG_FILES) do
|
||||
result = Fog::AWS[:rds].describe_db_log_files(@db_instance_id).body['DescribeDBLogFilesResult']
|
||||
returns(true) { result['DBLogFiles'].size > 0 }
|
||||
result
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
tests('failures') do
|
||||
raises(Fog::AWS::RDS::NotFound) {Fog::AWS[:rds].describe_db_log_files('doesntexist')}
|
||||
end
|
||||
end
|
19
tests/aws/requests/sts/assume_role_tests.rb
Normal file
19
tests/aws/requests/sts/assume_role_tests.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
Shindo.tests('AWS::STS | assume role', ['aws']) do
|
||||
|
||||
@policy = {"Statement" => [{"Effect" => "Allow", "Action" => "*", "Resource" => "*"}]}
|
||||
|
||||
@response_format = {
|
||||
'SessionToken' => String,
|
||||
'SecretAccessKey' => String,
|
||||
'Expiration' => String,
|
||||
'AccessKeyId' => String,
|
||||
'Arn' => String,
|
||||
'RequestId' => String
|
||||
}
|
||||
|
||||
tests("#assume_role('rolename', 'assumed_role_session', 'external_id', #{@policy.inspect}, 900)").formats(@response_format) do
|
||||
pending if Fog.mocking?
|
||||
Fog::AWS[:sts].assume_role("rolename","assumed_role_session","external_id", @policy, 900).body
|
||||
end
|
||||
|
||||
end
|
52
tests/openstack/requests/metering/meter_tests.rb
Normal file
52
tests/openstack/requests/metering/meter_tests.rb
Normal file
|
@ -0,0 +1,52 @@
|
|||
Shindo.tests('Fog::Metering[:openstack] | meter requests', ['openstack']) do
|
||||
|
||||
@sample_format = {
|
||||
'counter_name' => String,
|
||||
'user_id' => String,
|
||||
'resource_id' => String,
|
||||
'timestamp' => String,
|
||||
'resource_metadata' => Hash,
|
||||
'source' => String,
|
||||
'counter_unit' => String,
|
||||
'counter_volume' => Float,
|
||||
'project_id' => String,
|
||||
'message_id' => String,
|
||||
'counter_type' => String
|
||||
}
|
||||
|
||||
@meter_format = {
|
||||
'user_id' => String,
|
||||
'name' => String,
|
||||
'resource_id' => String,
|
||||
'project_id' => String,
|
||||
'type' => String,
|
||||
'unit' => String
|
||||
}
|
||||
|
||||
@statistics_format = {
|
||||
'count' => Integer,
|
||||
'duration_start' => String,
|
||||
'min' => Float,
|
||||
'max' => Float,
|
||||
'duration_end' => String,
|
||||
'period' => Integer,
|
||||
'period_end' => String,
|
||||
'duration' => Float,
|
||||
'period_start' => String,
|
||||
'avg' => Float,
|
||||
'sum' => Float
|
||||
}
|
||||
tests('success') do
|
||||
tests('#list_meters').formats([@meter_format]) do
|
||||
Fog::Metering[:openstack].list_meters.body
|
||||
end
|
||||
|
||||
tests('#get_samples').formats([@sample_format]) do
|
||||
Fog::Metering[:openstack].get_samples('test').body
|
||||
end
|
||||
|
||||
tests('#get_statistics').formats([@statistics_format]) do
|
||||
Fog::Metering[:openstack].get_statistics('test').body
|
||||
end
|
||||
end
|
||||
end
|
19
tests/openstack/requests/metering/resource_tests.rb
Normal file
19
tests/openstack/requests/metering/resource_tests.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
Shindo.tests('Fog::Metering[:openstack] | resource requests', ['openstack']) do
|
||||
|
||||
@resource_format = {
|
||||
'resource_id' => String,
|
||||
'project_id' => String,
|
||||
'user_id' => String,
|
||||
'metadata' => Hash,
|
||||
}
|
||||
|
||||
tests('success') do
|
||||
tests('#list_resource').formats([@resource_format]) do
|
||||
Fog::Metering[:openstack].list_resources.body
|
||||
end
|
||||
|
||||
tests('#get_resource').formats(@resource_format) do
|
||||
Fog::Metering[:openstack].get_resource('test').body
|
||||
end
|
||||
end
|
||||
end
|
|
@ -101,4 +101,13 @@ Shindo.tests('Fog::Rackspace::BlockStorage', ['rackspace']) do
|
|||
end
|
||||
end
|
||||
|
||||
tests('reauthentication') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
@service = Fog::Rackspace::BlockStorage.new :rackspace_region => :ord
|
||||
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
|
||||
@service.instance_variable_set("@auth_token", "bad-token")
|
||||
returns(200) { @service.list_volumes.status }
|
||||
end
|
||||
|
||||
end
|
|
@ -87,4 +87,13 @@ Shindo.tests('Rackspace | Compute', ['rackspace']) do
|
|||
returns(true, "uses custom endpoint") { (@service.instance_variable_get("@uri").host =~ /snet-/) != nil }
|
||||
end
|
||||
end
|
||||
|
||||
tests('reauthentication') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
@service = Fog::Compute::Rackspace.new
|
||||
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
|
||||
@service.instance_variable_set("@auth_token", "bad-token")
|
||||
returns(true) { [200, 203].include?(@service.list_flavors.status) }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -100,4 +100,13 @@ Shindo.tests('Fog::Compute::RackspaceV2', ['rackspace']) do
|
|||
end
|
||||
end
|
||||
|
||||
tests('reauthentication') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
@service = Fog::Compute::RackspaceV2.new
|
||||
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
|
||||
@service.instance_variable_set("@auth_token", "bad_token")
|
||||
returns(true) { [200, 203].include? @service.list_flavors.status }
|
||||
end
|
||||
|
||||
end
|
|
@ -102,6 +102,15 @@ Shindo.tests('Fog::Rackspace::Databases', ['rackspace']) do |variable|
|
|||
end
|
||||
end
|
||||
|
||||
tests('reauthentication') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
@service = Fog::Rackspace::Databases.new
|
||||
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
|
||||
@service.instance_variable_set("@auth_token", "bad_token")
|
||||
returns(200) { @service.list_flavors.status }
|
||||
end
|
||||
|
||||
@service = Fog::Rackspace::Databases.new
|
||||
|
||||
tests('#flavors').succeeds do
|
||||
|
|
|
@ -82,6 +82,15 @@ Shindo.tests('Fog::DNS::Rackspace', ['rackspace']) do
|
|||
end
|
||||
end
|
||||
|
||||
tests('reauthentication') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
@service =Fog::DNS::Rackspace.new
|
||||
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
|
||||
@service.instance_variable_set("@auth_token", "bad_token")
|
||||
returns(200) { @service.list_domains.status }
|
||||
end
|
||||
|
||||
tests('array_to_query_string') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
|
|
25
tests/rackspace/identity_tests.rb
Normal file
25
tests/rackspace/identity_tests.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
Shindo.tests('Fog::Rackspace::Identity', ['rackspace']) do
|
||||
|
||||
tests('current authentication') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
tests('variables populated').returns(200) do
|
||||
@service = Fog::Rackspace::Identity.new :rackspace_auth_url => 'https://identity.api.rackspacecloud.com/v2.0', :connection_options => {:ssl_verify_peer => true}
|
||||
returns(true, "auth token populated") { !@service.auth_token.nil? }
|
||||
returns(false, "path populated") { @service.instance_variable_get("@uri").host.nil? }
|
||||
returns(false, "service catalog populated") { @service.service_catalog.nil? }
|
||||
|
||||
@service.list_tenants.status
|
||||
end
|
||||
end
|
||||
|
||||
tests('reauthentication') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
@service = Fog::Rackspace::Identity.new :rackspace_region => :ord
|
||||
returns(true, "auth token populated") { !@service.auth_token.nil? }
|
||||
@service.instance_variable_set("@auth_token", "bad-token")
|
||||
returns(true) { [200, 203].include? @service.list_tenants.status }
|
||||
end
|
||||
|
||||
end
|
|
@ -101,6 +101,16 @@ Shindo.tests('Fog::Rackspace::LoadBalancers', ['rackspace']) do
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
tests('reauthentication') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
@service = Fog::Rackspace::LoadBalancers.new
|
||||
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
|
||||
@service.instance_variable_set("@auth_token", "bad-token")
|
||||
returns(200) { @service.list_load_balancers.status }
|
||||
end
|
||||
|
||||
pending if Fog.mocking?
|
||||
|
||||
@service = Fog::Rackspace::LoadBalancers.new
|
||||
|
|
|
@ -18,4 +18,39 @@ Shindo.tests('Fog::Rackspace', ['rackspace']) do
|
|||
end
|
||||
end
|
||||
|
||||
tests('json_response?') do
|
||||
returns(false, "nil") { Fog::Rackspace.json_response?(nil) }
|
||||
|
||||
tests('missing header').returns(false) do
|
||||
response = Excon::Response.new
|
||||
response.headers = nil #maybe this is a forced case
|
||||
returns(true) { response.headers.nil? }
|
||||
Fog::Rackspace.json_response?(response)
|
||||
end
|
||||
|
||||
tests('nil Content-Type header').returns(false) do
|
||||
response = Excon::Response.new
|
||||
response.headers['Content-Type'] = nil
|
||||
Fog::Rackspace.json_response?(response)
|
||||
end
|
||||
|
||||
tests('text/html Content-Type header').returns(false) do
|
||||
response = Excon::Response.new
|
||||
response.headers['Content-Type'] = 'text/html'
|
||||
Fog::Rackspace.json_response?(response)
|
||||
end
|
||||
|
||||
tests('application/json Content-Type header').returns(true) do
|
||||
response = Excon::Response.new
|
||||
response.headers['Content-Type'] = 'application/json'
|
||||
Fog::Rackspace.json_response?(response)
|
||||
end
|
||||
|
||||
tests('APPLICATION/JSON Content-Type header').returns(true) do
|
||||
response = Excon::Response.new
|
||||
response.headers['Content-Type'] = 'APPLICATION/JSON'
|
||||
Fog::Rackspace.json_response?(response)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -41,7 +41,7 @@ Shindo.tests('Fog::Rackspace::Identity | users', ['rackspace']) do
|
|||
|
||||
service = Fog::Rackspace::Identity.new
|
||||
id = nil
|
||||
username = 'foguser'
|
||||
username = "fog#{Time.now.to_i.to_s}"
|
||||
email = 'fog_user@example.com'
|
||||
enabled = true
|
||||
password = 'Fog_password1'
|
||||
|
@ -57,6 +57,9 @@ Shindo.tests('Fog::Rackspace::Identity | users', ['rackspace']) do
|
|||
service.delete_user(id)
|
||||
end
|
||||
|
||||
# there appears to be a werid caching issue. It's just easier to create a new username and continue on
|
||||
username = "fog#{Time.now.to_i.to_s}"
|
||||
|
||||
tests('#create_user with password').succeeds do
|
||||
data = service.create_user(username, email, enabled, :password => password ).body
|
||||
id = data['user']['id']
|
||||
|
|
|
@ -1,106 +1,350 @@
|
|||
Shindo.tests('Fog::Storage[:rackspace] | large object requests', ["rackspace"]) do
|
||||
Shindo.tests('Fog::Storage[:rackspace] | large object requests', ['rackspace']) do
|
||||
|
||||
unless Fog.mocking?
|
||||
@directory = Fog::Storage[:rackspace].directories.create(:key => 'foglargeobjecttests')
|
||||
@directory = Fog::Storage[:rackspace].directories.create(:key => 'foglargeobjecttests')
|
||||
@directory2 = Fog::Storage[:rackspace].directories.create(:key => 'foglargeobjecttests2')
|
||||
@segments = {
|
||||
:a => {
|
||||
:container => @directory.identity,
|
||||
:name => 'fog_large_object/a',
|
||||
:data => 'a' * (1024**2 + 10),
|
||||
:size => 1024**2 + 10,
|
||||
:etag => 'c2e97007d59f0c19b850debdcb80cca5'
|
||||
},
|
||||
:b => {
|
||||
:container => @directory.identity,
|
||||
:name => 'fog_large_object/b',
|
||||
:data => 'b' * (1024**2 + 20),
|
||||
:size => 1024**2 + 20,
|
||||
:etag => 'd35f50622a1259daad75ff7d5512c7ef'
|
||||
},
|
||||
:c => {
|
||||
:container => @directory.identity,
|
||||
:name => 'fog_large_object2/a',
|
||||
:data => 'c' * (1024**2 + 30),
|
||||
:size => 1024**2 + 30,
|
||||
:etag => '901d3531a87d188041d4d5b43cb464c1'
|
||||
},
|
||||
:d => {
|
||||
:container => @directory2.identity,
|
||||
:name => 'fog_large_object2/b',
|
||||
:data => 'd' * (1024**2 + 40),
|
||||
:size => 1024**2 + 40,
|
||||
:etag => '350c0e00525198813920a157df185c8d'
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
tests('success') do
|
||||
|
||||
tests("#put_object('foglargeobjecttests', 'fog_large_object/1', ('x' * 4 * 1024 * 1024))").succeeds do
|
||||
tests('upload test segments').succeeds do
|
||||
pending if Fog.mocking?
|
||||
Fog::Storage[:rackspace].put_object(@directory.identity, 'fog_large_object/1', ('x' * 4 * 1024 * 1024))
|
||||
|
||||
@segments.each_value do |segment|
|
||||
Fog::Storage[:rackspace].put_object(segment[:container], segment[:name], segment[:data])
|
||||
end
|
||||
end
|
||||
|
||||
tests("#put_object('foglargeobjecttests', 'fog_large_object/2', ('x' * 2 * 1024 * 1024))").succeeds do
|
||||
tests('dynamic large object requests') do
|
||||
pending if Fog.mocking?
|
||||
Fog::Storage[:rackspace].put_object(@directory.identity, 'fog_large_object/2', ('x' * 2 * 1024 * 1024))
|
||||
end
|
||||
|
||||
tests("#put_object('foglargeobjecttests', 'fog_large_object2/1', ('x' * 1 * 1024 * 1024))").succeeds do
|
||||
pending if Fog.mocking?
|
||||
Fog::Storage[:rackspace].put_object(@directory.identity, 'fog_large_object2/1', ('x' * 1 * 1024 * 1024))
|
||||
end
|
||||
|
||||
tests("using default X-Object-Manifest header") do
|
||||
|
||||
tests("#put_object_manifest('foglargeobjecttests', 'fog_large_object')").succeeds do
|
||||
pending if Fog.mocking?
|
||||
tests('#put_object_manifest alias').succeeds do
|
||||
Fog::Storage[:rackspace].put_object_manifest(@directory.identity, 'fog_large_object')
|
||||
end
|
||||
|
||||
tests("#get_object streams all segments matching the default prefix").succeeds do
|
||||
pending if Fog.mocking?
|
||||
Fog::Storage[:rackspace].get_object(@directory.identity, 'fog_large_object').body == ('x' * 7 * 1024 * 1024)
|
||||
tests('using default X-Object-Manifest header') do
|
||||
|
||||
tests('#put_dynamic_obj_manifest').succeeds do
|
||||
Fog::Storage[:rackspace].put_dynamic_obj_manifest(@directory.identity, 'fog_large_object')
|
||||
end
|
||||
|
||||
tests('#get_object streams all segments matching the default prefix').succeeds do
|
||||
expected = @segments[:a][:data] + @segments[:b][:data] + @segments[:c][:data]
|
||||
Fog::Storage[:rackspace].get_object(@directory.identity, 'fog_large_object').body == expected
|
||||
end
|
||||
|
||||
# When the manifest object name is equal to the segment prefix, OpenStack treats it as if it's the first segment.
|
||||
# So you must prepend the manifest object's Etag - Digest::MD5.hexdigest('')
|
||||
tests('#head_object returns Etag that includes manifest object in calculation').succeeds do
|
||||
etags = ['d41d8cd98f00b204e9800998ecf8427e', @segments[:a][:etag], @segments[:b][:etag], @segments[:c][:etag]]
|
||||
expected = "\"#{ Digest::MD5.hexdigest(etags.join) }\"" # returned in quotes "\"2577f38428e895c50de6ea78ccc7da2a"\"
|
||||
Fog::Storage[:rackspace].head_object(@directory.identity, 'fog_large_object').headers['Etag'] == expected
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
tests("#head_object returns Etag that includes manifest object in calculation").succeeds do
|
||||
pending if Fog.mocking?
|
||||
tests('specifying X-Object-Manifest segment prefix') do
|
||||
|
||||
tests('#put_dynamic_obj_manifest').succeeds do
|
||||
options = { 'X-Object-Manifest' => "#{ @directory.identity }/fog_large_object/" }
|
||||
Fog::Storage[:rackspace].put_dynamic_obj_manifest(@directory.identity, 'fog_large_object', options)
|
||||
end
|
||||
|
||||
tests('#get_object streams segments only matching the specified prefix').succeeds do
|
||||
expected = @segments[:a][:data] + @segments[:b][:data]
|
||||
Fog::Storage[:rackspace].get_object(@directory.identity, 'fog_large_object').body == expected
|
||||
end
|
||||
|
||||
tests('#head_object returns Etag that does not include manifest object in calculation').succeeds do
|
||||
etags = [@segments[:a][:etag], @segments[:b][:etag]]
|
||||
expected = "\"#{ Digest::MD5.hexdigest(etags.join) }\"" # returned in quotes "\"0f035ed3cc38aa0ef46dda3478fad44d"\"
|
||||
Fog::Storage[:rackspace].head_object(@directory.identity, 'fog_large_object').headers['Etag'] == expected
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
tests('storing manifest in a different container than the segments') do
|
||||
|
||||
tests('#put_dynamic_obj_manifest').succeeds do
|
||||
options = { 'X-Object-Manifest' => "#{ @directory.identity }/fog_large_object/" }
|
||||
Fog::Storage[:rackspace].put_dynamic_obj_manifest(@directory2.identity, 'fog_large_object', options)
|
||||
end
|
||||
|
||||
tests('#get_object').succeeds do
|
||||
expected = @segments[:a][:data] + @segments[:b][:data]
|
||||
Fog::Storage[:rackspace].get_object(@directory2.identity, 'fog_large_object').body == expected
|
||||
end
|
||||
|
||||
etags = []
|
||||
# When the manifest object name is equal to the prefix, OpenStack treats it as if it's the first segment.
|
||||
etags << Digest::MD5.hexdigest('') # Etag for manifest object => "d41d8cd98f00b204e9800998ecf8427e"
|
||||
etags << Digest::MD5.hexdigest('x' * 4 * 1024 * 1024) # => "44981362d3ba9b5bacaf017c2f29d355"
|
||||
etags << Digest::MD5.hexdigest('x' * 2 * 1024 * 1024) # => "67b2f816a30e8956149b2d7beb479e51"
|
||||
etags << Digest::MD5.hexdigest('x' * 1 * 1024 * 1024) # => "b561f87202d04959e37588ee05cf5b10"
|
||||
expected = Digest::MD5.hexdigest(etags.join) # => "42e92048bd2c8085e7072b0b55fd76ab"
|
||||
actual = Fog::Storage[:rackspace].head_object(@directory.identity, 'fog_large_object').headers['Etag']
|
||||
actual.gsub('"', '') == expected # actual is returned in quotes "\"42e92048bd2c8085e7072b0b55fd76abu"\"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
tests("specifying X-Object-Manifest segment prefix") do
|
||||
tests('static large object requests') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
tests('single container') do
|
||||
|
||||
tests('#put_static_obj_manifest').succeeds do
|
||||
segments = [
|
||||
{ :path => "#{ @segments[:a][:container] }/#{ @segments[:a][:name] }",
|
||||
:etag => @segments[:a][:etag],
|
||||
:size_bytes => @segments[:a][:size] },
|
||||
{ :path => "#{ @segments[:c][:container] }/#{ @segments[:c][:name] }",
|
||||
:etag => @segments[:c][:etag],
|
||||
:size_bytes => @segments[:c][:size] }
|
||||
]
|
||||
Fog::Storage[:rackspace].put_static_obj_manifest(@directory.identity, 'fog_large_object', segments)
|
||||
end
|
||||
|
||||
tests('#head_object') do
|
||||
etags = [@segments[:a][:etag], @segments[:c][:etag]]
|
||||
etag = "\"#{ Digest::MD5.hexdigest(etags.join) }\"" # "\"ad7e633a12e8a4915b45e6dd1d4b0b4b\""
|
||||
content_length = (@segments[:a][:size] + @segments[:c][:size]).to_s
|
||||
response = Fog::Storage[:rackspace].head_object(@directory.identity, 'fog_large_object')
|
||||
|
||||
returns(etag, 'returns ETag computed from segments') { response.headers['Etag'] }
|
||||
returns(content_length , 'returns Content-Length for all segments') { response.headers['Content-Length'] }
|
||||
returns('True', 'returns X-Static-Large-Object header') { response.headers['X-Static-Large-Object'] }
|
||||
end
|
||||
|
||||
tests('#get_object').succeeds do
|
||||
expected = @segments[:a][:data] + @segments[:c][:data]
|
||||
Fog::Storage[:rackspace].get_object(@directory.identity, 'fog_large_object').body == expected
|
||||
end
|
||||
|
||||
tests('#delete_static_large_object') do
|
||||
expected = {
|
||||
'Number Not Found' => 0,
|
||||
'Response Status' => '200 OK',
|
||||
'Errors' => [],
|
||||
'Number Deleted' => 3,
|
||||
'Response Body' => ''
|
||||
}
|
||||
returns(expected, 'deletes manifest and segments') do
|
||||
Fog::Storage[:rackspace].delete_static_large_object(@directory.identity, 'fog_large_object').body
|
||||
end
|
||||
end
|
||||
|
||||
tests("#put_object_manifest('foglargeobjecttests', 'fog_large_object', {'X-Object-Manifest' => 'foglargeobjecttests/fog_large_object/')").succeeds do
|
||||
pending if Fog.mocking?
|
||||
Fog::Storage[:rackspace].put_object_manifest(@directory.identity, 'fog_large_object', {'X-Object-Manifest' => "#{@directory.identity}/fog_large_object/"})
|
||||
end
|
||||
|
||||
tests("#get_object streams segments only matching the specified prefix").succeeds do
|
||||
pending if Fog.mocking?
|
||||
Fog::Storage[:rackspace].get_object(@directory.identity, 'fog_large_object').body == ('x' * 6 * 1024 * 1024)
|
||||
tests('multiple containers') do
|
||||
|
||||
tests('#put_static_obj_manifest').succeeds do
|
||||
segments = [
|
||||
{ :path => "#{ @segments[:b][:container] }/#{ @segments[:b][:name] }",
|
||||
:etag => @segments[:b][:etag],
|
||||
:size_bytes => @segments[:b][:size] },
|
||||
{ :path => "#{ @segments[:d][:container] }/#{ @segments[:d][:name] }",
|
||||
:etag => @segments[:d][:etag],
|
||||
:size_bytes => @segments[:d][:size] }
|
||||
]
|
||||
Fog::Storage[:rackspace].put_static_obj_manifest(@directory2.identity, 'fog_large_object', segments)
|
||||
end
|
||||
|
||||
tests('#head_object') do
|
||||
etags = [@segments[:b][:etag], @segments[:d][:etag]]
|
||||
etag = "\"#{ Digest::MD5.hexdigest(etags.join) }\"" # "\"9801a4cc4472896a1e975d03f0d2c3f8\""
|
||||
content_length = (@segments[:b][:size] + @segments[:d][:size]).to_s
|
||||
response = Fog::Storage[:rackspace].head_object(@directory2.identity, 'fog_large_object')
|
||||
|
||||
returns(etag, 'returns ETag computed from segments') { response.headers['Etag'] }
|
||||
returns(content_length , 'returns Content-Length for all segments') { response.headers['Content-Length'] }
|
||||
returns('True', 'returns X-Static-Large-Object header') { response.headers['X-Static-Large-Object'] }
|
||||
end
|
||||
|
||||
tests('#get_object').succeeds do
|
||||
expected = @segments[:b][:data] + @segments[:d][:data]
|
||||
Fog::Storage[:rackspace].get_object(@directory2.identity, 'fog_large_object').body == expected
|
||||
end
|
||||
|
||||
tests('#delete_static_large_object') do
|
||||
expected = {
|
||||
'Number Not Found' => 0,
|
||||
'Response Status' => '200 OK',
|
||||
'Errors' => [],
|
||||
'Number Deleted' => 3,
|
||||
'Response Body' => ''
|
||||
}
|
||||
returns(expected, 'deletes manifest and segments') do
|
||||
Fog::Storage[:rackspace].delete_static_large_object(@directory2.identity, 'fog_large_object').body
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
tests("#head_object returns Etag that does not include manifest object in calculation").succeeds do
|
||||
pending if Fog.mocking?
|
||||
|
||||
etags = []
|
||||
etags << Digest::MD5.hexdigest('x' * 4 * 1024 * 1024) # => "44981362d3ba9b5bacaf017c2f29d355"
|
||||
etags << Digest::MD5.hexdigest('x' * 2 * 1024 * 1024) # => "67b2f816a30e8956149b2d7beb479e51"
|
||||
expected = Digest::MD5.hexdigest(etags.join) # => "0b348495a774eaa4d4c4bbf770820f84"
|
||||
actual = Fog::Storage[:rackspace].head_object(@directory.identity, 'fog_large_object').headers['Etag']
|
||||
actual.gsub('"', '') == expected # actual is returned in quotes "\"0b348495a774eaa4d4c4bbf770820f84"\"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
tests("storing manifest object in a different container than the segments") do
|
||||
|
||||
tests("#put_object_manifest('foglargeobjecttests2', 'fog_large_object', {'X-Object-Manifest' => 'foglargeobjecttests/fog_large_object/'})").succeeds do
|
||||
pending if Fog.mocking?
|
||||
Fog::Storage[:rackspace].put_object_manifest(@directory2.identity, 'fog_large_object', {'X-Object-Manifest' => "#{@directory.identity}/fog_large_object/"})
|
||||
end
|
||||
|
||||
tests("#get_object('foglargeobjecttests2', 'fog_large_object').body").succeeds do
|
||||
pending if Fog.mocking?
|
||||
Fog::Storage[:rackspace].get_object(@directory2.identity, 'fog_large_object').body == ('x' * 6 * 1024 * 1024)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
unless Fog.mocking?
|
||||
['fog_large_object', 'fog_large_object/1', 'fog_large_object/2', 'fog_large_object2/1'].each do |key|
|
||||
@directory.files.new(:key => key).destroy
|
||||
end
|
||||
@directory2.files.new(:key => 'fog_large_object').destroy
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
tests('failure') do
|
||||
|
||||
tests("put_object_manifest")
|
||||
tests('dynamic large object requests') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
tests('#put_dynamic_obj_manifest with missing container').raises(Fog::Storage::Rackspace::NotFound) do
|
||||
Fog::Storage[:rackspace].put_dynamic_obj_manifest('fognoncontainer', 'fog_large_object')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
tests('static large object requests') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
tests('upload test segments').succeeds do
|
||||
Fog::Storage[:rackspace].put_object(@segments[:a][:container], @segments[:a][:name], @segments[:a][:data])
|
||||
Fog::Storage[:rackspace].put_object(@segments[:b][:container], @segments[:b][:name], @segments[:b][:data])
|
||||
end
|
||||
|
||||
tests('#put_static_obj_manifest with missing container').raises(Fog::Storage::Rackspace::NotFound) do
|
||||
Fog::Storage[:rackspace].put_static_obj_manifest('fognoncontainer', 'fog_large_object', [])
|
||||
end
|
||||
|
||||
tests('#put_static_obj_manifest with missing object') do
|
||||
segments = [
|
||||
{ :path => "#{ @segments[:c][:container] }/#{ @segments[:c][:name] }",
|
||||
:etag => @segments[:c][:etag],
|
||||
:size_bytes => @segments[:c][:size] }
|
||||
]
|
||||
expected = { 'Errors' => [[segments[0][:path], '404 Not Found']] }
|
||||
|
||||
error = nil
|
||||
begin
|
||||
Fog::Storage[:rackspace].put_static_obj_manifest(@directory.identity, 'fog_large_object', segments)
|
||||
rescue => err
|
||||
error = err
|
||||
end
|
||||
|
||||
raises(Fog::Storage::Rackspace::BadRequest) do
|
||||
raise error if error
|
||||
end
|
||||
|
||||
expected['Errors'][0][0] = error.response_data['Errors'][0][0] rescue nil
|
||||
returns(expected, 'returns error information') do
|
||||
error.response_data
|
||||
end
|
||||
end
|
||||
|
||||
tests('#put_static_obj_manifest with invalid etag') do
|
||||
segments = [
|
||||
{ :path => "#{ @segments[:a][:container] }/#{ @segments[:a][:name] }",
|
||||
:etag => @segments[:b][:etag],
|
||||
:size_bytes => @segments[:a][:size] }
|
||||
]
|
||||
expected = { 'Errors' => [[segments[0][:path], 'Etag Mismatch']] }
|
||||
|
||||
error = nil
|
||||
begin
|
||||
Fog::Storage[:rackspace].put_static_obj_manifest(@directory.identity, 'fog_large_object', segments)
|
||||
rescue => err
|
||||
error = err
|
||||
end
|
||||
|
||||
raises(Fog::Storage::Rackspace::BadRequest) do
|
||||
raise error if error
|
||||
end
|
||||
|
||||
expected['Errors'][0][0] = error.response_data['Errors'][0][0] rescue nil
|
||||
returns(expected, 'returns error information') do
|
||||
error.response_data
|
||||
end
|
||||
end
|
||||
|
||||
tests('#put_static_obj_manifest with invalid byte_size') do
|
||||
segments = [
|
||||
{ :path => "#{ @segments[:a][:container] }/#{ @segments[:a][:name] }",
|
||||
:etag => @segments[:a][:etag],
|
||||
:size_bytes => @segments[:b][:size] }
|
||||
]
|
||||
expected = { 'Errors' => [[segments[0][:path], 'Size Mismatch']] }
|
||||
|
||||
error = nil
|
||||
begin
|
||||
Fog::Storage[:rackspace].put_static_obj_manifest(@directory.identity, 'fog_large_object', segments)
|
||||
rescue => err
|
||||
error = err
|
||||
end
|
||||
|
||||
raises(Fog::Storage::Rackspace::BadRequest) do
|
||||
raise error if error
|
||||
end
|
||||
|
||||
expected['Errors'][0][0] = error.response_data['Errors'][0][0] rescue nil
|
||||
returns(expected, 'returns error information') do
|
||||
error.response_data
|
||||
end
|
||||
end
|
||||
|
||||
tests('#delete_static_large_object with missing container').raises(Fog::Storage::Rackspace::NotFound) do
|
||||
Fog::Storage[:rackspace].delete_static_large_object('fognoncontainer', 'fog_large_object')
|
||||
end
|
||||
|
||||
tests('#delete_static_large_object with missing manifest').raises(Fog::Storage::Rackspace::NotFound) do
|
||||
Fog::Storage[:rackspace].delete_static_large_object(@directory.identity, 'fog_non_object')
|
||||
end
|
||||
|
||||
tests('#delete_static_large_object with missing segment') do
|
||||
|
||||
tests('#put_static_obj_manifest for segments :a and :b').succeeds do
|
||||
segments = [
|
||||
{ :path => "#{ @segments[:a][:container] }/#{ @segments[:a][:name] }",
|
||||
:etag => @segments[:a][:etag],
|
||||
:size_bytes => @segments[:a][:size] },
|
||||
{ :path => "#{ @segments[:b][:container] }/#{ @segments[:b][:name] }",
|
||||
:etag => @segments[:b][:etag],
|
||||
:size_bytes => @segments[:b][:size] }
|
||||
]
|
||||
Fog::Storage[:rackspace].put_static_obj_manifest(@directory.identity, 'fog_large_object', segments)
|
||||
end
|
||||
|
||||
tests('#delete_object segment :b').succeeds do
|
||||
Fog::Storage[:rackspace].delete_object(@segments[:b][:container], @segments[:b][:name])
|
||||
end
|
||||
|
||||
tests('#delete_static_large_object') do
|
||||
expected = {
|
||||
'Number Not Found' => 1,
|
||||
'Response Status' => '200 OK',
|
||||
'Errors' => [],
|
||||
'Number Deleted' => 2,
|
||||
'Response Body' => ''
|
||||
}
|
||||
returns(expected, 'deletes manifest and segment :a, and reports missing segment :b') do
|
||||
Fog::Storage[:rackspace].delete_static_large_object(@directory.identity, 'fog_large_object').body
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -17,18 +17,18 @@ Shindo.tests('Fog::Storage[:rackspace] | object requests', ["rackspace"]) do
|
|||
Fog::Storage[:rackspace].put_object('fogobjecttests', 'fog_object', lorem_file)
|
||||
end
|
||||
|
||||
tests("#get_object('fogobjectests', 'fog_object')").returns(lorem_file.read) do
|
||||
tests("#get_object('fogobjectests', 'fog_object')").succeeds do
|
||||
pending if Fog.mocking?
|
||||
Fog::Storage[:rackspace].get_object('fogobjecttests', 'fog_object').body
|
||||
Fog::Storage[:rackspace].get_object('fogobjecttests', 'fog_object').body == lorem_file.read
|
||||
end
|
||||
|
||||
tests("#get_object('fogobjecttests', 'fog_object', &block)").returns(lorem_file.read) do
|
||||
tests("#get_object('fogobjecttests', 'fog_object', &block)").succeeds do
|
||||
pending if Fog.mocking?
|
||||
data = ''
|
||||
Fog::Storage[:rackspace].get_object('fogobjecttests', 'fog_object') do |chunk, remaining_bytes, total_bytes|
|
||||
data << chunk
|
||||
end
|
||||
data
|
||||
data == lorem_file.read
|
||||
end
|
||||
|
||||
tests("#head_object('fogobjectests', 'fog_object')").succeeds do
|
||||
|
@ -70,40 +70,59 @@ Shindo.tests('Fog::Storage[:rackspace] | object requests', ["rackspace"]) do
|
|||
storage = Fog::Storage::Rackspace.new(:rackspace_temp_url_key => "super_secret")
|
||||
storage.extend RackspaceStorageHelpers
|
||||
storage.override_path('/fake_version/fake_tenant')
|
||||
object_url = storage.get_object_https_url('fogobjecttests', 'fog-object', expires_at)
|
||||
object_url = storage.get_object_https_url('fogobjecttests', 'fog-object', expires_at)
|
||||
object_url =~ /https:\/\/.*clouddrive.com\/[^\/]+\/[^\/]+\/fogobjecttests\/fog%2Dobject\?temp_url_sig=a24dd5fc955a57adce7d1b5bc4ec2c7660ab8396&temp_url_expires=1344149532/
|
||||
end
|
||||
|
||||
tests("put_object with block") do
|
||||
tests("#put_object('fogobjecttests', 'fog_object', &block)") do
|
||||
pending if Fog.mocking?
|
||||
pending if Fog.mocking?
|
||||
|
||||
tests("#put_object('fogobjecttests', 'fog_object', &block)").succeeds do
|
||||
begin
|
||||
file = lorem_file
|
||||
buffer_size = file.size / 2 # chop it up into two buffers
|
||||
buffer_size = file.stat.size / 2 # chop it up into two buffers
|
||||
Fog::Storage[:rackspace].put_object('fogobjecttests', 'fog_block_object', nil) do
|
||||
if file.pos < file.size
|
||||
file.sysread(buffer_size)
|
||||
else
|
||||
""
|
||||
end
|
||||
file.read(buffer_size).to_s
|
||||
end
|
||||
ensure
|
||||
file.close
|
||||
end
|
||||
end
|
||||
|
||||
tests("object successfully uploaded?").returns(lorem_file.read) do
|
||||
pending if Fog.mocking?
|
||||
Fog::Storage[:rackspace].get_object('fogobjecttests', 'fog_block_object').body
|
||||
tests('#get_object').succeeds do
|
||||
Fog::Storage[:rackspace].get_object('fogobjecttests', 'fog_block_object').body == lorem_file.read
|
||||
end
|
||||
|
||||
tests("delete file").succeeds do
|
||||
pending if Fog.mocking?
|
||||
tests('#delete_object').succeeds do
|
||||
Fog::Storage[:rackspace].delete_object('fogobjecttests', 'fog_block_object')
|
||||
end
|
||||
end
|
||||
|
||||
tests('#delete_multiple_objects') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
Fog::Storage[:rackspace].put_object('fogobjecttests', 'fog_object', lorem_file)
|
||||
Fog::Storage[:rackspace].put_object('fogobjecttests', 'fog_object2', lorem_file)
|
||||
Fog::Storage[:rackspace].directories.create(:key => 'fogobjecttests2')
|
||||
Fog::Storage[:rackspace].put_object('fogobjecttests2', 'fog_object', lorem_file)
|
||||
|
||||
expected = {
|
||||
"Number Not Found" => 0,
|
||||
"Response Status" => "200 OK",
|
||||
"Errors" => [],
|
||||
"Number Deleted" => 2,
|
||||
"Response Body" => ""
|
||||
}
|
||||
|
||||
returns(expected, 'deletes multiple objects') do
|
||||
Fog::Storage[:rackspace].delete_multiple_objects('fogobjecttests', ['fog_object', 'fog_object2']).body
|
||||
end
|
||||
|
||||
returns(expected, 'deletes object and container') do
|
||||
Fog::Storage[:rackspace].delete_multiple_objects(nil, ['fogobjecttests2/fog_object', 'fogobjecttests2']).body
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
tests('failure') do
|
||||
|
@ -138,6 +157,42 @@ Shindo.tests('Fog::Storage[:rackspace] | object requests', ["rackspace"]) do
|
|||
Fog::Storage[:rackspace].delete_object('fognoncontainer', 'fog_non_object')
|
||||
end
|
||||
|
||||
tests('#delete_multiple_objects') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
expected = {
|
||||
"Number Not Found" => 2,
|
||||
"Response Status" => "200 OK",
|
||||
"Errors" => [],
|
||||
"Number Deleted" => 0,
|
||||
"Response Body" => ""
|
||||
}
|
||||
|
||||
returns(expected, 'reports missing objects') do
|
||||
Fog::Storage[:rackspace].delete_multiple_objects('fogobjecttests', ['fog_non_object', 'fog_non_object2']).body
|
||||
end
|
||||
|
||||
returns(expected, 'reports missing container') do
|
||||
Fog::Storage[:rackspace].delete_multiple_objects('fognoncontainer', ['fog_non_object', 'fog_non_object2']).body
|
||||
end
|
||||
|
||||
tests('deleting non-empty container') do
|
||||
Fog::Storage[:rackspace].put_object('fogobjecttests', 'fog_object', lorem_file)
|
||||
|
||||
expected = {
|
||||
"Number Not Found" => 0,
|
||||
"Response Status" => "400 Bad Request",
|
||||
"Errors" => [['fogobjecttests', '409 Conflict']],
|
||||
"Number Deleted" => 1,
|
||||
"Response Body" => ""
|
||||
}
|
||||
|
||||
body = Fog::Storage[:rackspace].delete_multiple_objects(nil, ['fogobjecttests', 'fogobjecttests/fog_object']).body
|
||||
expected['Errors'][0][0] = body['Errors'][0][0] rescue nil
|
||||
returns(expected, 'deletes object but not container') { body }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
unless Fog.mocking?
|
||||
|
|
83
tests/rackspace/service_tests.rb
Normal file
83
tests/rackspace/service_tests.rb
Normal file
|
@ -0,0 +1,83 @@
|
|||
Shindo.tests('Fog::Rackspace::Service', ['rackspace']) do
|
||||
|
||||
tests('process_response') do
|
||||
@service = Fog::Rackspace::Service.new
|
||||
|
||||
tests('nil').returns(nil) do
|
||||
@service.send(:process_response, nil)
|
||||
end
|
||||
|
||||
tests('response missing body').returns(nil) do
|
||||
response = Excon::Response.new
|
||||
response.body = nil
|
||||
@service.send(:process_response, response)
|
||||
end
|
||||
|
||||
tests('processes body').returns({'a'=>2, 'b'=>3}) do
|
||||
response = Excon::Response.new
|
||||
response.headers['Content-Type'] = "application/json"
|
||||
response.body = "{\"a\":2,\"b\":3}"
|
||||
@service.send(:process_response, response)
|
||||
response.body
|
||||
end
|
||||
|
||||
tests('process body with hash').returns({:a=>2, :b=>3}) do
|
||||
response = Excon::Response.new
|
||||
response.headers['Content-Type'] = "application/json"
|
||||
response.body = {:a=>2, :b=>3}
|
||||
@service.send(:process_response, response)
|
||||
response.body
|
||||
end
|
||||
|
||||
tests('handles malformed json').returns({}) do
|
||||
response = Excon::Response.new
|
||||
response.headers['Content-Type'] = "application/json"
|
||||
response.body = "I am totally not json"
|
||||
@service.send(:process_response, response)
|
||||
response.body
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
tests('headers') do
|
||||
# adding an implementation for auth_token to service instance. Normally this would come from a subclass.
|
||||
def @service.auth_token
|
||||
"my_auth_token"
|
||||
end
|
||||
|
||||
HEADER_HASH = {
|
||||
'Content-Type' => 'application/json',
|
||||
'Accept' => 'application/json',
|
||||
'X-Auth-Token' => @service.auth_token
|
||||
}.freeze
|
||||
|
||||
tests('without options').returns(HEADER_HASH) do
|
||||
@service.send(:headers)
|
||||
end
|
||||
|
||||
tests('with options not containing :header key').returns(HEADER_HASH) do
|
||||
@service.send(:headers, {:a => 3})
|
||||
end
|
||||
|
||||
tests('with options containing :header key').returns(HEADER_HASH.merge(:a => 3)) do
|
||||
@service.send(:headers, :headers => {:a => 3})
|
||||
end
|
||||
end
|
||||
|
||||
tests('request_params') do
|
||||
REQUEST_HASH = {
|
||||
:path=>"/endpoint/my_service",
|
||||
:headers=>{"Content-Type"=>"application/json", "Accept"=>"application/json", "X-Auth-Token"=>"my_auth_token"},
|
||||
:host=>"fog.io"
|
||||
}.freeze
|
||||
|
||||
uri = URI.parse("http://fog.io/endpoint")
|
||||
@service.instance_variable_set("@uri", uri)
|
||||
params = {:path => 'my_service'}
|
||||
|
||||
tests('returns request hash').returns(REQUEST_HASH) do
|
||||
@service.send(:request_params, params)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -98,6 +98,15 @@ Shindo.tests('Rackspace | Storage', ['rackspace']) do
|
|||
returns(true, "uses custom endpoint") { (@service.instance_variable_get("@uri").host =~ /snet-/) != nil }
|
||||
end
|
||||
end
|
||||
|
||||
tests('reauthentication') do
|
||||
pending if Fog.mocking?
|
||||
|
||||
@service = Fog::Storage::Rackspace.new
|
||||
returns(true, "auth token populated") { !@service.send(:auth_token).nil? }
|
||||
@service.instance_variable_set("@auth_token", "bad-token")
|
||||
returns(204) { @service.head_containers.status }
|
||||
end
|
||||
|
||||
tests('account').succeeds do
|
||||
pending if Fog.mocking?
|
||||
|
|
Loading…
Add table
Reference in a new issue