cleanups + refactorings + better error reporting per joyent cloudapi spec

This commit is contained in:
Kevin Chan 2012-02-27 23:14:11 -08:00
parent 92b18651c7
commit d9c2cc0036
2 changed files with 146 additions and 22 deletions

View File

@ -1,4 +1,5 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'brightbox'))
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'joyent'))
require File.expand_path(File.join(File.dirname(__FILE__), 'errors'))
require 'fog/compute'
require 'multi_json'
@ -71,6 +72,8 @@ module Fog
request :delete_machine_tag
request :delete_all_machine_tags
class Mock
def self.data
@data ||= Hash.new do |hash, key|
@ -113,14 +116,15 @@ module Fog
@rsa = OpenSSL::PKey::RSA.new(@joyent_key)
@header_method = method(:header_for_signature)
@header_method = method(:header_for_signature_auth)
else
raise ArgumentError, "options[:joyent_keyfile] provided does not exist."
end
elsif options[:joyent_password]
@joyent_password = options[:joyent_password]
@header_method = method(:header_for_basic)
@header_method = method(:header_for_basic_auth)
else
raise ArgumentError, "Must provide either a joyent_password or joyent_keyname and joyent_keyfile pair"
end
@ -132,36 +136,42 @@ module Fog
)
end
def request(request_options = {})
(request_options[:headers] ||= {}).merge!({
def request(request = {})
request[:headers] = {
"X-Api-Version" => @joyent_version,
"Content-Type" => "application/json",
"Accept" => "application/json"
}).merge!(@header_method.call)
}.merge(request[:headers] || {}).merge(@header_method.call)
if request_options[:body]
request_options[:body] = MultiJson.encode(request_options[:body])
if request[:body]
request[:body] = MultiJson.encode(request[:body])
end
response = @connection.request(request_options)
response = @connection.request(request)
if response.headers["Content-Type"] == "application/json"
response.body = MultiJson.decode(response.body)
response.body = decode_time_props(response.body)
response.body = json_decode(response.body)
end
raise_if_error!(request, response)
response
end
private
def header_for_basic
def json_decode(body)
parsed = MultiJson.decode(body)
decode_time_attrs(parsed)
end
def header_for_basic_auth
{
"Authorization" => "Basic #{Base64.encode64("#{@joyent_username}:#{@joyent_password}").delete("\r\n")}"
}
end
def header_for_signature
def header_for_signature_auth
date = Time.now.utc.httpdate
signature = Base64.encode64(@rsa.sign("sha256", date)).delete("\r\n")
key_id = "/#{@joyent_username}/keys/#{@joyent_keyname}"
@ -172,22 +182,46 @@ module Fog
}
end
def decode_time_props(obj)
def decode_time_attrs(obj)
if obj.kind_of?(Hash)
if obj["created"]
obj["created"] = Time.parse(obj["created"])
end
if obj["updated"]
obj["updated"] = Time.parse(obj["updated"])
end
obj["created"] = Time.parse(obj["created"]) if obj["created"]
obj["updated"] = Time.parse(obj["updated"]) if obj["updated"]
elsif obj.kind_of?(Array)
obj.map do |o|
decode_time_props(o)
decode_time_attrs(o)
end
end
obj
end
def raise_if_error!(request, response)
case response.status
when 401 then
raise Errors::Unauthorized.new('Invalid credentials were used', request, response)
when 403 then
raise Errors::Forbidden.new('No permissions to the specified resource', request, response)
when 404 then
raise Errors::NotFound.new('Requested resource was not found', request, response)
when 405 then
raise Errors::MethodNotAllowed.new('Method not supported for the given resource', request, response)
when 406 then
raise Errors::NotAcceptable.new('Try sending a different Accept header', request, response)
when 409 then
raise Errors::Conflict.new('Most likely invalid or missing parameters', request, response)
when 414 then
raise Errors::RequestEntityTooLarge.new('You sent too much data', request, response)
when 415 then
raise Errors::UnsupportedMediaType.new('You encoded your request in a format we don\'t understand', request, response)
when 420 then
raise Errors::PolicyNotForfilled.new('You are sending too many requests', request, response)
when 449 then
raise Errors::RetryWith.new('Invalid API Version requested; try with a different API Version', request, response)
when 503 then
raise Errors::ServiceUnavailable.new('Either there\'s no capacity in this datacenter, or we\'re in a maintenance window', request, response)
end
end
end # Real
end
end

90
lib/fog/joyent/errors.rb Normal file
View File

@ -0,0 +1,90 @@
module Fog
module Compute
class Joyent < Fog::Service
class Errors
module MessageParserMixin
def message
if response.body["code"] && response.body["message"]
"[ERROR #{response.body['code']}] : #{response.body['message']}"
else
''
end
end
def to_s
message
end
end
# https://us-west-1.api.joyentcloud.com/docs#cloudapi-http-responses
#
# HTTP Status Codes
#
# Your client should check for each of the following status codes from any API request:
#
# Response Code Description
# 400 Bad Request Invalid HTTP Request
class BadRequest < Excon::Errors::BadRequest
include MessageParserMixin
end
# 401 Unauthorized Either no Authorization header was sent, or invalid credentials were used
class Unauthorized < Excon::Errors::Unauthorized
include MessageParserMixin
end
# 403 Forbidden No permissions to the specified resource
class Forbidden < Excon::Errors::Forbidden
include MessageParserMixin
end
# 404 Not Found Something you requested was not found
class NotFound < Excon::Errors::NotFound
include MessageParserMixin
end
# 405 Method Not Allowed Method not supported for the given resource
class MethodNotAllowed < Excon::Errors::MethodNotAllowed
include MessageParserMixin
end
# 406 Not Acceptable Try sending a different Accept header
class NotAcceptable < Excon::Errors::NotAcceptable
include MessageParserMixin
end
# 409 Conflict Most likely invalid or missing parameters
class Conflict < Excon::Errors::Conflict
include MessageParserMixin
end
# 413 Request Entity Too Large You sent too much data
class RequestEntityTooLarge < Excon::Errors::RequestEntityTooLarge
include MessageParserMixin
end
# 415 Unsupported Media Type You encoded your request in a format we don't understand
class UnsupportedMediaType < Excon::Errors::UnsupportedMediaType
include MessageParserMixin
end
# 420 Slow Down You're sending too many requests
class PolicyNotForfilled < Excon::Errors::HTTPStatusError
include MessageParserMixin
end
# 449 Retry With Invalid Version header; try with a different X-Api-Version string
class RetryWith < Excon::Errors::HTTPStatusError
include MessageParserMixin
end
# 503 Service Unavailable Either there's no capacity in this datacenter, or we're in a maintenance window
class ServiceUnavailable < Excon::Errors::ServiceUnavailable
include MessageParserMixin
end
end
end
end
end