mirror of
https://github.com/fog/fog.git
synced 2022-11-09 13:51:43 -05:00
0e1daf3ddd
Unlike last attempt this replaces Fog::Connection with Fog::XML::Connection which should be directly compatible. Fog::Connection is there for old PRs but should be removed real soon. Providers using JSON should be able to replace "XML" with "Core" within their code to cut down on the dependency. If I get the time I may attempt to clean up some but testing with Mock will mean that is mostly educated guesswork.
186 lines
5.4 KiB
Ruby
186 lines
5.4 KiB
Ruby
require 'fog/atmos/core'
|
|
|
|
module Fog
|
|
module Storage
|
|
class Atmos < Fog::Service
|
|
requires :atmos_storage_endpoint,
|
|
:atmos_storage_secret,
|
|
:atmos_storage_token
|
|
recognizes :persistent
|
|
|
|
model_path 'fog/atmos/models/storage'
|
|
model :directory
|
|
collection :directories
|
|
model :file
|
|
collection :files
|
|
|
|
request_path 'fog/atmos/requests/storage'
|
|
# request :delete_container
|
|
request :get_namespace
|
|
request :head_namespace
|
|
request :post_namespace
|
|
request :put_namespace
|
|
request :delete_namespace
|
|
|
|
module Utils
|
|
ENDPOINT_REGEX = /(https*):\/\/([a-zA-Z0-9\.\-]+)(:[0-9]+)?(\/.*)?/
|
|
|
|
def ssl?
|
|
protocol = @endpoint.match(ENDPOINT_REGEX)[1]
|
|
raise ArgumentError, 'Invalid endpoint URL' if protocol.nil?
|
|
|
|
return true if protocol == 'https'
|
|
return false if protocol == 'http'
|
|
|
|
raise ArgumentError, "Unknown protocol #{protocol}"
|
|
end
|
|
|
|
def port
|
|
port = @endpoint.match(ENDPOINT_REGEX)[3]
|
|
return ssl? ? 443 : 80 if port.nil?
|
|
port.split(':')[1].to_i
|
|
end
|
|
|
|
def host
|
|
@endpoint.match(ENDPOINT_REGEX)[2]
|
|
end
|
|
|
|
def api_path
|
|
@endpoint.match(ENDPOINT_REGEX)[4]
|
|
end
|
|
|
|
def setup_credentials(options)
|
|
@storage_token = options[:atmos_storage_token]
|
|
@storage_secret = options[:atmos_storage_secret]
|
|
@storage_secret_decoded = Base64.decode64(@storage_secret)
|
|
@endpoint = options[:atmos_storage_endpoint]
|
|
@prefix = self.ssl? ? 'https' : 'http'
|
|
@storage_host = self.host
|
|
@storage_port = self.port
|
|
@api_path = self.api_path
|
|
end
|
|
end
|
|
|
|
class Mock
|
|
include Utils
|
|
|
|
def initialize(options={})
|
|
setup_credentials(options)
|
|
end
|
|
|
|
def request(options)
|
|
raise "Atmos Storage mocks not implemented"
|
|
end
|
|
|
|
end
|
|
|
|
class Real
|
|
include Utils
|
|
|
|
def initialize(options={})
|
|
setup_credentials(options)
|
|
@connection_options = options[:connection_options] || {}
|
|
@hmac = Fog::HMAC.new('sha1', @storage_secret_decoded)
|
|
@persistent = options.fetch(:persistent, false)
|
|
|
|
@connection = Fog::XML::Connection.new("#{@prefix}://#{@storage_host}:#{@storage_port}",
|
|
@persistent, @connection_options)
|
|
end
|
|
|
|
def uid
|
|
@storage_token#.split('/')[-1]
|
|
end
|
|
|
|
def sign(string)
|
|
value = @hmac.sign(string)
|
|
Base64.encode64( value ).chomp()
|
|
end
|
|
|
|
def reload
|
|
@connection.reset
|
|
end
|
|
|
|
def request(params, &block)
|
|
req_path = params[:path]
|
|
# Force set host and port
|
|
params.merge!({
|
|
:host => @storage_host,
|
|
:path => "#{@api_path}/rest/#{params[:path]}",
|
|
})
|
|
# Set default method and headers
|
|
params = {:method => 'GET', :headers => {}}.merge params
|
|
|
|
params[:headers]["Content-Type"] ||= "application/octet-stream"
|
|
|
|
# Add request date
|
|
params[:headers]["date"] = Time.now().httpdate()
|
|
params[:headers]["x-emc-uid"] = @storage_token
|
|
|
|
# Build signature string
|
|
signstring = ""
|
|
signstring += params[:method]
|
|
signstring += "\n"
|
|
signstring += params[:headers]["Content-Type"]
|
|
signstring += "\n"
|
|
if( params[:headers]["range"] )
|
|
signstring += params[:headers]["range"]
|
|
end
|
|
signstring += "\n"
|
|
signstring += params[:headers]["date"]
|
|
signstring += "\n"
|
|
|
|
signstring += "/rest/" + URI.unescape( req_path ).downcase
|
|
query_str = params[:query].map{|k,v| "#{k}=#{v}"}.join('&')
|
|
signstring += '?' + query_str unless query_str.empty?
|
|
signstring += "\n"
|
|
|
|
customheaders = {}
|
|
params[:headers].each { |key,value|
|
|
case key
|
|
when 'x-emc-date', 'x-emc-signature'
|
|
#skip
|
|
when /^x-emc-/
|
|
customheaders[ key.downcase ] = value
|
|
end
|
|
}
|
|
header_arr = customheaders.sort()
|
|
|
|
header_arr.each { |key,value|
|
|
# Values are lowercase and whitespace-normalized
|
|
signstring += key + ":" + value.strip.chomp.squeeze( " " ) + "\n"
|
|
}
|
|
|
|
digest = @hmac.sign(signstring.chomp())
|
|
signature = Base64.encode64( digest ).chomp()
|
|
params[:headers]["x-emc-signature"] = signature
|
|
|
|
params.delete(:host) #invalid excon request parameter
|
|
|
|
parse = params.delete(:parse)
|
|
|
|
begin
|
|
response = @connection.request(params, &block)
|
|
rescue Excon::Errors::HTTPStatusError => error
|
|
raise case error
|
|
when Excon::Errors::NotFound
|
|
Fog::Storage::Atmos::NotFound.slurp(error)
|
|
else
|
|
error
|
|
end
|
|
end
|
|
unless response.body.empty?
|
|
if parse
|
|
document = Fog::ToHashDocument.new
|
|
parser = Nokogiri::XML::SAX::PushParser.new(document)
|
|
parser << response.body
|
|
parser.finish
|
|
response.body = document.body
|
|
end
|
|
end
|
|
response
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
end
|