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

[rackspace] updated services to re-authenticate when authentication token expires.

This commit is contained in:
Kyle Rames 2013-06-21 15:11:17 -05:00
parent 3b760d6838
commit 04394eb054
20 changed files with 336 additions and 211 deletions

View file

@ -96,6 +96,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 " "

View file

@ -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)
super(params)
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

View file

@ -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)
super(params)
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

View file

@ -141,15 +141,7 @@ module Fog
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]}"
}))
super(params)
rescue Excon::Errors::NotFound => error
raise NotFound.slurp(error, region)
rescue Excon::Errors::BadRequest => error
@ -159,25 +151,15 @@ module Fog
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
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

View file

@ -87,15 +87,7 @@ module Fog
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]}"
}))
super(params)
rescue Excon::Errors::NotFound => error
raise NotFound.slurp(error, region)
rescue Excon::Errors::BadRequest => error
@ -105,25 +97,19 @@ module Fog
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 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

View file

@ -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
@ -109,24 +112,21 @@ module Fog
private
def request(params)
#TODO - Unify code with other rackspace services
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)
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)
@ -158,15 +158,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

View file

@ -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']

View file

@ -120,17 +120,8 @@ module Fog
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]}"
}))
super(params)
rescue Excon::Errors::NotFound => error
raise NotFound.slurp(error, region)
rescue Excon::Errors::BadRequest => error
@ -140,20 +131,15 @@ module Fog
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 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

View file

@ -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

View file

@ -138,38 +138,16 @@ module Fog
@connection.reset
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
def request(params, parse_json = true)
super(params)
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?

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -82,4 +82,13 @@ 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
end

View 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

View file

@ -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

View file

@ -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

View 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

View file

@ -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?