mirror of
https://github.com/fog/fog.git
synced 2022-11-09 13:51:43 -05:00
Merge pull request #10 from SDK-CLI-Docs/feature/93_cross_tenant_acls
Feature/93 cross tenant acls
This commit is contained in:
commit
24d5dac17e
16 changed files with 731 additions and 81 deletions
|
@ -20,8 +20,13 @@ module Fog
|
||||||
data = nil
|
data = nil
|
||||||
message = nil
|
message = nil
|
||||||
else
|
else
|
||||||
data = MultiJson.decode(error.response.body)
|
begin
|
||||||
message = data['message']
|
data = MultiJson.decode(error.response.body)
|
||||||
|
message = data['message']
|
||||||
|
rescue MultiJson::DecodeError
|
||||||
|
data = error.response.body #### the body is not in JSON format so just return it as it is
|
||||||
|
message = data
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
new_error = super(error, message)
|
new_error = super(error, message)
|
||||||
|
@ -33,6 +38,7 @@ module Fog
|
||||||
class InternalServerError < ServiceError; end
|
class InternalServerError < ServiceError; end
|
||||||
class Conflict < ServiceError; end
|
class Conflict < ServiceError; end
|
||||||
class NotFound < ServiceError; end
|
class NotFound < ServiceError; end
|
||||||
|
class Forbidden < ServiceError; end
|
||||||
class ServiceUnavailable < ServiceError; end
|
class ServiceUnavailable < ServiceError; end
|
||||||
|
|
||||||
class BadRequest < ServiceError
|
class BadRequest < ServiceError
|
||||||
|
|
|
@ -15,24 +15,8 @@ module Fog
|
||||||
end
|
end
|
||||||
|
|
||||||
def head(key, options = {})
|
def head(key, options = {})
|
||||||
read_header = nil
|
|
||||||
write_header = nil
|
|
||||||
data = connection.head_container(key)
|
data = connection.head_container(key)
|
||||||
directory = new(:key => key)
|
directory = create_directory(key, data)
|
||||||
for key, value in data.headers
|
|
||||||
if ['X-Container-Bytes-Used', 'X-Container-Object-Count'].include?(key)
|
|
||||||
directory.merge_attributes(key => value)
|
|
||||||
end
|
|
||||||
if key == 'X-Container-Read'
|
|
||||||
read_header = value
|
|
||||||
elsif key == 'X-Container-Write'
|
|
||||||
write_header = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# set the acl on the directory based on the headers
|
|
||||||
if !(read_header.nil? && write_header.nil?)
|
|
||||||
directory.acl = connection.header_to_acl(read_header, write_header)
|
|
||||||
end
|
|
||||||
# set the cdn state for the directory
|
# set the cdn state for the directory
|
||||||
directory.cdn_enabled?
|
directory.cdn_enabled?
|
||||||
|
|
||||||
|
@ -42,24 +26,9 @@ module Fog
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(key, options = {})
|
def get(key, options = {})
|
||||||
read_header = nil
|
|
||||||
write_header = nil
|
|
||||||
data = connection.get_container(key, options)
|
data = connection.get_container(key, options)
|
||||||
directory = new(:key => key)
|
directory = create_directory(key, data)
|
||||||
for key, value in data.headers
|
# set the files for the directory
|
||||||
if ['X-Container-Bytes-Used', 'X-Container-Object-Count'].include?(key)
|
|
||||||
directory.merge_attributes(key => value)
|
|
||||||
end
|
|
||||||
if key == 'X-Container-Read'
|
|
||||||
read_header = value
|
|
||||||
elsif key == 'X-Container-Write'
|
|
||||||
write_header = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# set the acl on the directory based on the headers
|
|
||||||
if !(read_header.nil? && write_header.nil?)
|
|
||||||
directory.acl = connection.header_to_acl(read_header, write_header)
|
|
||||||
end
|
|
||||||
directory.files.merge_attributes(options)
|
directory.files.merge_attributes(options)
|
||||||
directory.files.instance_variable_set(:@loaded, true)
|
directory.files.instance_variable_set(:@loaded, true)
|
||||||
data.body.each do |file|
|
data.body.each do |file|
|
||||||
|
@ -73,6 +42,32 @@ module Fog
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def create_directory(key, data)
|
||||||
|
read_header = nil
|
||||||
|
write_header = nil
|
||||||
|
directory = new(:key => key)
|
||||||
|
for key, value in data.headers
|
||||||
|
if ['X-Container-Bytes-Used', 'X-Container-Object-Count'].include?(key)
|
||||||
|
directory.merge_attributes(key => value)
|
||||||
|
end
|
||||||
|
if key == 'X-Container-Read'
|
||||||
|
read_header = value
|
||||||
|
elsif key == 'X-Container-Write'
|
||||||
|
write_header = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# set the acl on the directory based on the headers
|
||||||
|
if !(read_header.nil? && write_header.nil?)
|
||||||
|
read_acl, write_acl = connection.header_to_perm_acl(read_header, write_header)
|
||||||
|
# do not want to expose the read_acl and write_acl as writable attributes
|
||||||
|
directory.instance_variable_set(:@read_acl, read_acl)
|
||||||
|
directory.instance_variable_set(:@write_acl, write_acl)
|
||||||
|
end
|
||||||
|
directory
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,15 +12,70 @@ module Fog
|
||||||
attribute :bytes, :aliases => 'X-Container-Bytes-Used'
|
attribute :bytes, :aliases => 'X-Container-Bytes-Used'
|
||||||
attribute :count, :aliases => 'X-Container-Object-Count'
|
attribute :count, :aliases => 'X-Container-Object-Count'
|
||||||
|
|
||||||
def acl=(new_acl)
|
def initialize(attributes = {})
|
||||||
if new_acl.nil?
|
@read_acl = []
|
||||||
new_acl = "private"
|
@write_acl = []
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_acl
|
||||||
|
@read_acl
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_acl
|
||||||
|
@write_acl
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_read?(user)
|
||||||
|
return false if @read_acl.nil?
|
||||||
|
list_users_with_read.include?(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_write?(user)
|
||||||
|
return false if @write_acl.nil?
|
||||||
|
list_users_with_write.include?(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_read_write?(user)
|
||||||
|
can_read?(user) && can_write?(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_users_with_read
|
||||||
|
users = []
|
||||||
|
users = @read_acl.map {|acl| acl.split(':')[1]} unless @read_acl.nil?
|
||||||
|
return users
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_users_with_write
|
||||||
|
users = []
|
||||||
|
users = @write_acl.map {|acl| acl.split(':')[1]} unless @write_acl.nil?
|
||||||
|
return users
|
||||||
|
end
|
||||||
|
|
||||||
|
def grant(perm, users=[])
|
||||||
|
r_acl, w_acl = connection.perm_to_acl(perm, users)
|
||||||
|
unless r_acl.nil?
|
||||||
|
@read_acl = @read_acl + r_acl
|
||||||
|
@read_acl.uniq!
|
||||||
end
|
end
|
||||||
valid_acls = ['private', 'public-read', 'public-write', 'public-read-write']
|
unless w_acl.nil?
|
||||||
unless valid_acls.include?(new_acl)
|
@write_acl = @write_acl + w_acl
|
||||||
raise ArgumentError.new("acl must be one of [#{valid_acls.join(', ')}]")
|
@write_acl.uniq!
|
||||||
end
|
end
|
||||||
@acl = new_acl
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def revoke(perm, users=[])
|
||||||
|
r_acl, w_acl = connection.perm_to_acl(perm, users)
|
||||||
|
unless r_acl.nil?
|
||||||
|
@read_acl = @read_acl - r_acl
|
||||||
|
@read_acl.uniq!
|
||||||
|
end
|
||||||
|
unless w_acl.nil?
|
||||||
|
@write_acl = @write_acl - w_acl
|
||||||
|
@write_acl.uniq!
|
||||||
|
end
|
||||||
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
|
@ -50,18 +105,20 @@ module Fog
|
||||||
|
|
||||||
def public=(new_public)
|
def public=(new_public)
|
||||||
if new_public
|
if new_public
|
||||||
@acl = 'public-read'
|
self.grant("pr")
|
||||||
else
|
else
|
||||||
@acl = 'private'
|
self.revoke("pr")
|
||||||
end
|
end
|
||||||
@public = new_public
|
@public = new_public
|
||||||
end
|
end
|
||||||
|
|
||||||
def public?
|
def public?
|
||||||
if @acl.nil?
|
if @read_acl.empty?
|
||||||
false
|
false
|
||||||
|
elsif @read_acl.include?(".r:*")
|
||||||
|
true
|
||||||
else
|
else
|
||||||
@acl == 'public-read'
|
false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -70,7 +127,7 @@ module Fog
|
||||||
@public_url ||= begin
|
@public_url ||= begin
|
||||||
begin response = connection.head_container(key)
|
begin response = connection.head_container(key)
|
||||||
# escape the key to cover for special char. in container names
|
# escape the key to cover for special char. in container names
|
||||||
url = "#{connection.url}/#{Fog::HP.escape(key)}"
|
url = connection.public_url(key)
|
||||||
rescue Fog::Storage::HP::NotFound => err
|
rescue Fog::Storage::HP::NotFound => err
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
@ -135,9 +192,8 @@ module Fog
|
||||||
def save
|
def save
|
||||||
requires :key
|
requires :key
|
||||||
options = {}
|
options = {}
|
||||||
if @acl
|
# write out the acls into the headers before save
|
||||||
options.merge!(connection.acl_to_header(@acl))
|
options.merge!(connection.perm_acl_to_header(@read_acl, @write_acl))
|
||||||
end
|
|
||||||
connection.put_container(key, options)
|
connection.put_container(key, options)
|
||||||
# Added an extra check to see if CDN is enabled for the container
|
# Added an extra check to see if CDN is enabled for the container
|
||||||
if (!connection.cdn.nil? && connection.cdn.enabled?)
|
if (!connection.cdn.nil? && connection.cdn.enabled?)
|
||||||
|
|
49
lib/fog/hp/models/storage/shared_directories.rb
Normal file
49
lib/fog/hp/models/storage/shared_directories.rb
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
require 'fog/core/collection'
|
||||||
|
require 'fog/hp/models/storage/shared_directory'
|
||||||
|
|
||||||
|
module Fog
|
||||||
|
module Storage
|
||||||
|
class HP
|
||||||
|
|
||||||
|
class SharedDirectories < Fog::Collection
|
||||||
|
|
||||||
|
model Fog::Storage::HP::SharedDirectory
|
||||||
|
|
||||||
|
def head(url)
|
||||||
|
data = connection.head_shared_container(url)
|
||||||
|
shared_directory = new(:url => url)
|
||||||
|
for key, value in data.headers
|
||||||
|
if ['X-Container-Bytes-Used', 'X-Container-Object-Count'].include?(key)
|
||||||
|
shared_directory.merge_attributes(key => value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_directory
|
||||||
|
rescue Fog::Storage::HP::NotFound, Fog::HP::Errors::Forbidden
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(url)
|
||||||
|
data = connection.get_shared_container(url)
|
||||||
|
shared_directory = new(:url => url)
|
||||||
|
for key, value in data.headers
|
||||||
|
if ['X-Container-Bytes-Used', 'X-Container-Object-Count'].include?(key)
|
||||||
|
shared_directory.merge_attributes(key => value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# set the files for the directory
|
||||||
|
shared_directory.files.instance_variable_set(:@loaded, true)
|
||||||
|
data.body.each do |file|
|
||||||
|
shared_directory.files << shared_directory.files.new(file)
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_directory
|
||||||
|
rescue Fog::Storage::HP::NotFound, Fog::HP::Errors::Forbidden
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
28
lib/fog/hp/models/storage/shared_directory.rb
Normal file
28
lib/fog/hp/models/storage/shared_directory.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
require 'fog/core/model'
|
||||||
|
require 'fog/hp/models/storage/shared_files'
|
||||||
|
|
||||||
|
module Fog
|
||||||
|
module Storage
|
||||||
|
class HP
|
||||||
|
|
||||||
|
class SharedDirectory < Fog::Model
|
||||||
|
|
||||||
|
identity :url
|
||||||
|
|
||||||
|
attribute :bytes, :aliases => 'X-Container-Bytes-Used'
|
||||||
|
attribute :count, :aliases => 'X-Container-Object-Count'
|
||||||
|
|
||||||
|
def files
|
||||||
|
@files ||= begin
|
||||||
|
Fog::Storage::HP::SharedFiles.new(
|
||||||
|
:shared_directory => self,
|
||||||
|
:connection => connection
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
57
lib/fog/hp/models/storage/shared_file.rb
Normal file
57
lib/fog/hp/models/storage/shared_file.rb
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
require 'fog/core/model'
|
||||||
|
|
||||||
|
module Fog
|
||||||
|
module Storage
|
||||||
|
class HP
|
||||||
|
|
||||||
|
class SharedFile < Fog::Model
|
||||||
|
|
||||||
|
identity :key, :aliases => 'name'
|
||||||
|
attribute :url
|
||||||
|
|
||||||
|
attribute :content_length, :aliases => ['bytes', 'Content-Length'], :type => :integer
|
||||||
|
attribute :content_type, :aliases => ['content_type', 'Content-Type']
|
||||||
|
attribute :etag, :aliases => ['hash', 'Etag']
|
||||||
|
attribute :last_modified, :aliases => ['last_modified', 'Last-Modified'], :type => :time
|
||||||
|
|
||||||
|
def url
|
||||||
|
"#{self.collection.shared_directory.url}/#{key}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def body
|
||||||
|
attributes[:body] ||= if last_modified
|
||||||
|
collection.get(identity).body
|
||||||
|
else
|
||||||
|
''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def body=(new_body)
|
||||||
|
attributes[:body] = new_body
|
||||||
|
end
|
||||||
|
|
||||||
|
def shared_directory
|
||||||
|
@shared_directory
|
||||||
|
end
|
||||||
|
|
||||||
|
def save(options = {})
|
||||||
|
requires :shared_directory, :key
|
||||||
|
options['Content-Type'] = content_type if content_type
|
||||||
|
data = connection.put_shared_object(shared_directory.url, key, body, options)
|
||||||
|
merge_attributes(data.headers)
|
||||||
|
self.content_length = Fog::Storage.get_body_size(body)
|
||||||
|
true
|
||||||
|
rescue Fog::Storage::HP::NotFound, Fog::HP::Errors::Forbidden
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def shared_directory=(new_shared_directory)
|
||||||
|
@shared_directory = new_shared_directory
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
60
lib/fog/hp/models/storage/shared_files.rb
Normal file
60
lib/fog/hp/models/storage/shared_files.rb
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
require 'fog/core/collection'
|
||||||
|
require 'fog/hp/models/storage/shared_file'
|
||||||
|
|
||||||
|
module Fog
|
||||||
|
module Storage
|
||||||
|
class HP
|
||||||
|
|
||||||
|
class SharedFiles < Fog::Collection
|
||||||
|
|
||||||
|
attribute :shared_directory
|
||||||
|
|
||||||
|
model Fog::Storage::HP::SharedFile
|
||||||
|
|
||||||
|
def all
|
||||||
|
requires :shared_directory
|
||||||
|
parent = shared_directory.collection.get(shared_directory.url)
|
||||||
|
if parent
|
||||||
|
load(parent.files.map {|file| file.attributes})
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
rescue Fog::Storage::HP::NotFound, Fog::HP::Errors::Forbidden
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(key, &block)
|
||||||
|
requires :shared_directory
|
||||||
|
shared_object_url = "#{shared_directory.url}/#{key}"
|
||||||
|
data = connection.get_shared_object(shared_object_url, &block)
|
||||||
|
file_data = data.headers.merge({
|
||||||
|
:body => data.body,
|
||||||
|
:key => key
|
||||||
|
})
|
||||||
|
new(file_data)
|
||||||
|
rescue Fog::Storage::HP::NotFound, Fog::HP::Errors::Forbidden
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def head(key)
|
||||||
|
requires :shared_directory
|
||||||
|
shared_object_url = "#{shared_directory.url}/#{key}"
|
||||||
|
data = connection.head_shared_object(shared_object_url)
|
||||||
|
file_data = data.headers.merge({
|
||||||
|
:key => key
|
||||||
|
})
|
||||||
|
new(file_data)
|
||||||
|
rescue Fog::Storage::HP::NotFound, Fog::HP::Errors::Forbidden
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def new(attributes = {})
|
||||||
|
requires :shared_directory
|
||||||
|
super({ :shared_directory => shared_directory }.merge!(attributes))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
75
lib/fog/hp/requests/storage/get_shared_container.rb
Normal file
75
lib/fog/hp/requests/storage/get_shared_container.rb
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
module Fog
|
||||||
|
module Storage
|
||||||
|
class HP
|
||||||
|
class Real
|
||||||
|
|
||||||
|
# Get details for a shared container
|
||||||
|
#
|
||||||
|
# ==== Parameters
|
||||||
|
# * shared_container_url<~String> - Url of the shared container
|
||||||
|
# * options<~String>:
|
||||||
|
# * 'limit'<~String> - Maximum number of objects to return
|
||||||
|
# * 'marker'<~String> - Only return objects whose name is greater than marker
|
||||||
|
# * 'prefix'<~String> - Limits results to those starting with prefix
|
||||||
|
# * 'path'<~String> - Return objects nested in the pseudo path
|
||||||
|
#
|
||||||
|
# ==== Returns
|
||||||
|
# * response<~Excon::Response>:
|
||||||
|
# * headers<~Hash>:
|
||||||
|
# * 'X-Container-Object-Count'<~String> - Count of objects in container
|
||||||
|
# * 'X-Container-Bytes-Used'<~String> - Bytes used
|
||||||
|
# * 'X-Trans-Id'<~String> - Trans Id
|
||||||
|
# * body<~Array>:
|
||||||
|
# * item<~Hash>:
|
||||||
|
# * 'bytes'<~String> - Size of object
|
||||||
|
# * 'content_type'<~String> Content-Type of object
|
||||||
|
# * 'hash'<~String> - Hash of object (etag?)
|
||||||
|
# * 'last_modified'<~String> - Last modified timestamp
|
||||||
|
# * 'name'<~String> - Name of object
|
||||||
|
def get_shared_container(shared_container_url, options = {})
|
||||||
|
options = options.reject {|key, value| value.nil?}
|
||||||
|
# split up the shared container url
|
||||||
|
uri = URI.parse(shared_container_url)
|
||||||
|
path = uri.path
|
||||||
|
|
||||||
|
response = shared_request(
|
||||||
|
:expects => 200,
|
||||||
|
:method => 'GET',
|
||||||
|
:path => path,
|
||||||
|
:query => {'format' => 'json'}.merge!(options)
|
||||||
|
)
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class Mock # :nodoc:all
|
||||||
|
|
||||||
|
def get_shared_container(shared_container_url, options = {})
|
||||||
|
response = Excon::Response.new
|
||||||
|
data = {
|
||||||
|
'name' => Fog::Mock.random_letters(10),
|
||||||
|
'hash' => Fog::HP::Mock.etag,
|
||||||
|
'bytes' => 11,
|
||||||
|
'content_type' => "text/plain",
|
||||||
|
'last_modified' => Time.now
|
||||||
|
}
|
||||||
|
response.status = 200
|
||||||
|
response.body = [data]
|
||||||
|
response.headers = {
|
||||||
|
'X-Container-Object-Count' => 1,
|
||||||
|
'X-Container-Bytes-Used' => 11,
|
||||||
|
'Accept-Ranges' => 'bytes',
|
||||||
|
'Content-Type' => "application/json",
|
||||||
|
'Content-Length' => 11,
|
||||||
|
'X-Trans-Id' => "tx#{Fog::Mock.random_hex(32)}"
|
||||||
|
}
|
||||||
|
response
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
67
lib/fog/hp/requests/storage/get_shared_object.rb
Normal file
67
lib/fog/hp/requests/storage/get_shared_object.rb
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
module Fog
|
||||||
|
module Storage
|
||||||
|
class HP
|
||||||
|
class Real
|
||||||
|
|
||||||
|
# Get details for a shared object
|
||||||
|
#
|
||||||
|
# ==== Parameters
|
||||||
|
# * shared_object_url<~String> - Url of the shared object
|
||||||
|
#
|
||||||
|
def get_shared_object(shared_object_url, &block)
|
||||||
|
# split up the shared object url
|
||||||
|
uri = URI.parse(shared_object_url)
|
||||||
|
path = uri.path
|
||||||
|
|
||||||
|
if block_given?
|
||||||
|
response = shared_request(
|
||||||
|
:response_block => block,
|
||||||
|
:expects => 200,
|
||||||
|
:method => 'GET',
|
||||||
|
:path => path
|
||||||
|
)
|
||||||
|
else
|
||||||
|
response = shared_request({
|
||||||
|
:block => block,
|
||||||
|
:expects => 200,
|
||||||
|
:method => 'GET',
|
||||||
|
:path => path
|
||||||
|
}, false, &block)
|
||||||
|
end
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class Mock # :nodoc:all
|
||||||
|
|
||||||
|
def get_shared_object(shared_object_url, &block)
|
||||||
|
response = Excon::Response.new
|
||||||
|
response.status = 200
|
||||||
|
response.headers = {
|
||||||
|
'Last-Modified' => Date.today.rfc822,
|
||||||
|
'Etag' => Fog::HP::Mock.etag,
|
||||||
|
'Accept-Ranges' => 'bytes',
|
||||||
|
'Content-Type' => "text/plain",
|
||||||
|
'Content-Length' => 11,
|
||||||
|
'X-Trans-Id' => "tx#{Fog::Mock.random_hex(32)}"
|
||||||
|
}
|
||||||
|
unless block_given?
|
||||||
|
response.body = "This is a sample text.\n"
|
||||||
|
else
|
||||||
|
data = StringIO.new("This is a sample text.\n")
|
||||||
|
remaining = data.length
|
||||||
|
while remaining > 0
|
||||||
|
chunk = data.read([remaining, Excon::CHUNK_SIZE].min)
|
||||||
|
block.call(chunk)
|
||||||
|
remaining -= Excon::CHUNK_SIZE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -30,6 +30,7 @@ module Fog
|
||||||
def head_container(container_name)
|
def head_container(container_name)
|
||||||
response = get_container(container_name)
|
response = get_container(container_name)
|
||||||
response.body = nil
|
response.body = nil
|
||||||
|
response.status = 204
|
||||||
response
|
response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ module Fog
|
||||||
#
|
#
|
||||||
def head_object(container, object)
|
def head_object(container, object)
|
||||||
response = request({
|
response = request({
|
||||||
:expects => 200,
|
:expects => 200, # should be 204
|
||||||
:method => 'HEAD',
|
:method => 'HEAD',
|
||||||
:path => "#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}"
|
:path => "#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}"
|
||||||
}, false)
|
}, false)
|
||||||
|
@ -25,6 +25,7 @@ module Fog
|
||||||
def head_object(container_name, object_name, options = {})
|
def head_object(container_name, object_name, options = {})
|
||||||
response = get_object(container_name, object_name, options)
|
response = get_object(container_name, object_name, options)
|
||||||
response.body = nil
|
response.body = nil
|
||||||
|
response.status = 200 # should be 204
|
||||||
response
|
response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
45
lib/fog/hp/requests/storage/head_shared_container.rb
Normal file
45
lib/fog/hp/requests/storage/head_shared_container.rb
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
module Fog
|
||||||
|
module Storage
|
||||||
|
class HP
|
||||||
|
class Real
|
||||||
|
|
||||||
|
# List number of objects and total bytes stored for a shared container
|
||||||
|
#
|
||||||
|
# ==== Parameters
|
||||||
|
# * shared_container_url<~String> - Url of the shared container
|
||||||
|
#
|
||||||
|
# ==== Returns
|
||||||
|
# * response<~Excon::Response>:
|
||||||
|
# * headers<~Hash>:
|
||||||
|
# * 'X-Container-Object-Count'<~String> - Count of containers
|
||||||
|
# * 'X-Container-Bytes-Used'<~String> - Bytes used
|
||||||
|
def head_shared_container(shared_container_url)
|
||||||
|
# split up the shared container url
|
||||||
|
uri = URI.parse(shared_container_url)
|
||||||
|
path = uri.path
|
||||||
|
|
||||||
|
response = shared_request(
|
||||||
|
:expects => 204,
|
||||||
|
:method => 'HEAD',
|
||||||
|
:path => path,
|
||||||
|
:query => {'format' => 'json'}
|
||||||
|
)
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class Mock # :nodoc:all
|
||||||
|
|
||||||
|
def head_shared_container(shared_container_url)
|
||||||
|
response = get_shared_container(shared_container_url)
|
||||||
|
response.body = nil
|
||||||
|
response.status = 204
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
39
lib/fog/hp/requests/storage/head_shared_object.rb
Normal file
39
lib/fog/hp/requests/storage/head_shared_object.rb
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
module Fog
|
||||||
|
module Storage
|
||||||
|
class HP
|
||||||
|
class Real
|
||||||
|
|
||||||
|
# Get headers for shared object
|
||||||
|
#
|
||||||
|
# ==== Parameters
|
||||||
|
# * * shared_object_url<~String> - Url of the shared object
|
||||||
|
#
|
||||||
|
def head_shared_object(shared_object_url)
|
||||||
|
# split up the shared object url
|
||||||
|
uri = URI.parse(shared_object_url)
|
||||||
|
path = uri.path
|
||||||
|
|
||||||
|
response = shared_request({
|
||||||
|
:expects => 200, # should be 204
|
||||||
|
:method => 'HEAD',
|
||||||
|
:path => path
|
||||||
|
}, false)
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class Mock # :nodoc:all
|
||||||
|
|
||||||
|
def head_shared_object(shared_object_url)
|
||||||
|
response = get_shared_object(shared_object_url)
|
||||||
|
response.body = nil
|
||||||
|
response.status = 200 # should be 204
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -22,16 +22,16 @@ module Fog
|
||||||
|
|
||||||
class Mock # :nodoc:all
|
class Mock # :nodoc:all
|
||||||
def put_container(container_name, options = {})
|
def put_container(container_name, options = {})
|
||||||
acl = options['X-Container-Read'] || 'private'
|
read_h = options['X-Container-Read'] || ''
|
||||||
if !['private', 'public-read'].include?(acl)
|
write_h = options['X-Container-Write'] || ''
|
||||||
#raise Excon::Errors::BadRequest.new('invalid X-Container-Read')
|
unless options
|
||||||
else
|
read_acl, write_acl = self.class.header_to_perm_acl(read_h, write_h)
|
||||||
self.data[:acls][:container][container_name] = self.class.acls(acl)
|
self.data[:acls][:container][container_name] = {:read_acl => read_acl, :write_acl => write_acl}
|
||||||
end
|
end
|
||||||
|
|
||||||
response = Excon::Response.new
|
response = Excon::Response.new
|
||||||
container = {
|
container = {
|
||||||
:objects => {},
|
:objects => {}
|
||||||
}
|
}
|
||||||
if self.data[:containers][container_name]
|
if self.data[:containers][container_name]
|
||||||
response.status = 202
|
response.status = 202
|
||||||
|
|
85
lib/fog/hp/requests/storage/put_shared_object.rb
Normal file
85
lib/fog/hp/requests/storage/put_shared_object.rb
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
module Fog
|
||||||
|
module Storage
|
||||||
|
class HP
|
||||||
|
class Real
|
||||||
|
|
||||||
|
# Create a new object in a shared container
|
||||||
|
#
|
||||||
|
# ==== Parameters
|
||||||
|
# * shared_container_url<~String> - Shared url for the container
|
||||||
|
# * object<~String> - Name of the object
|
||||||
|
# * options<~Hash> - header options
|
||||||
|
#
|
||||||
|
def put_shared_object(shared_container_url, object_name, data, options = {}, &block)
|
||||||
|
# split up the shared object url
|
||||||
|
uri = URI.parse(shared_container_url)
|
||||||
|
path = uri.path
|
||||||
|
|
||||||
|
data = Fog::Storage.parse_data(data)
|
||||||
|
headers = data[:headers].merge!(options)
|
||||||
|
if block_given?
|
||||||
|
headers['Transfer-Encoding'] = 'chunked'
|
||||||
|
headers.delete('Content-Length')
|
||||||
|
return shared_request(
|
||||||
|
:request_block => block,
|
||||||
|
:expects => 201,
|
||||||
|
:headers => headers,
|
||||||
|
:method => 'PUT',
|
||||||
|
:path => "#{path}/#{Fog::HP.escape(object_name)}"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
if headers.has_key?('Transfer-Encoding')
|
||||||
|
headers.delete('Content-Length')
|
||||||
|
end
|
||||||
|
response = shared_request(
|
||||||
|
:body => data[:body],
|
||||||
|
:expects => 201,
|
||||||
|
:headers => headers,
|
||||||
|
:method => 'PUT',
|
||||||
|
:path => "#{path}/#{Fog::HP.escape(object_name)}"
|
||||||
|
)
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class Mock # :nodoc:all
|
||||||
|
|
||||||
|
def put_shared_object(shared_container_url, object_name, data, options = {}, &block)
|
||||||
|
response = Excon::Response.new
|
||||||
|
data = Fog::Storage.parse_data(data)
|
||||||
|
unless data[:body].is_a?(String)
|
||||||
|
data[:body] = data[:body].read
|
||||||
|
end
|
||||||
|
response.status = 201
|
||||||
|
object = {
|
||||||
|
:body => data[:body],
|
||||||
|
'Content-Type' => options['Content-Type'] || data[:headers]['Content-Type'],
|
||||||
|
'ETag' => Fog::HP::Mock.etag,
|
||||||
|
'Key' => object_name,
|
||||||
|
'Date' => Fog::Time.now.to_date_header,
|
||||||
|
'Content-Length' => options['Content-Length'] || data[:headers]['Content-Length'],
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value in options
|
||||||
|
case key
|
||||||
|
when 'Cache-Control', 'Content-Disposition', 'Content-Encoding', 'Content-MD5', 'Expires', /^X-Object-Meta-/
|
||||||
|
object[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
response.headers = {
|
||||||
|
'Content-Length' => object['Content-Length'],
|
||||||
|
'Content-Type' => object['Content-Type'],
|
||||||
|
'ETag' => object['ETag'],
|
||||||
|
'Date' => object['Date']
|
||||||
|
}
|
||||||
|
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -11,8 +11,12 @@ module Fog
|
||||||
model_path 'fog/hp/models/storage'
|
model_path 'fog/hp/models/storage'
|
||||||
model :directory
|
model :directory
|
||||||
collection :directories
|
collection :directories
|
||||||
|
model :shared_directory
|
||||||
|
collection :shared_directories
|
||||||
model :file
|
model :file
|
||||||
collection :files
|
collection :files
|
||||||
|
model :shared_file
|
||||||
|
collection :shared_files
|
||||||
|
|
||||||
request_path 'fog/hp/requests/storage'
|
request_path 'fog/hp/requests/storage'
|
||||||
request :delete_container
|
request :delete_container
|
||||||
|
@ -21,11 +25,16 @@ module Fog
|
||||||
request :get_containers
|
request :get_containers
|
||||||
request :get_object
|
request :get_object
|
||||||
request :get_object_temp_url
|
request :get_object_temp_url
|
||||||
|
request :get_shared_container
|
||||||
|
request :get_shared_object
|
||||||
request :head_container
|
request :head_container
|
||||||
request :head_containers
|
request :head_containers
|
||||||
request :head_object
|
request :head_object
|
||||||
|
request :head_shared_container
|
||||||
|
request :head_shared_object
|
||||||
request :put_container
|
request :put_container
|
||||||
request :put_object
|
request :put_object
|
||||||
|
request :put_shared_object
|
||||||
|
|
||||||
module Utils
|
module Utils
|
||||||
|
|
||||||
|
@ -53,34 +62,80 @@ module Fog
|
||||||
"#{@scheme}://#{@host}:#{@port}#{@path}"
|
"#{@scheme}://#{@host}:#{@port}#{@path}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def acl_to_header(acl)
|
def public_url(container=nil, object=nil)
|
||||||
|
public_url = nil
|
||||||
|
unless container.nil?
|
||||||
|
if object.nil?
|
||||||
|
# return container public url
|
||||||
|
public_url = "#{url}/#{Fog::HP.escape(container)}"
|
||||||
|
else
|
||||||
|
# return object public url
|
||||||
|
public_url = "#{url}/#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
public_url
|
||||||
|
end
|
||||||
|
|
||||||
|
def perm_to_acl(perm, users=[])
|
||||||
|
read_perm_acl = []
|
||||||
|
write_perm_acl = []
|
||||||
|
valid_public_perms = ['pr', 'pw', 'prw']
|
||||||
|
valid_account_perms = ['r', 'w', 'rw']
|
||||||
|
valid_perms = valid_public_perms + valid_account_perms
|
||||||
|
unless valid_perms.include?(perm)
|
||||||
|
raise ArgumentError.new("permission must be one of [#{valid_perms.join(', ')}]")
|
||||||
|
end
|
||||||
|
# tackle the public access differently
|
||||||
|
if valid_public_perms.include?(perm)
|
||||||
|
case perm
|
||||||
|
when "pr"
|
||||||
|
read_perm_acl = [".r:*",".rlistings"]
|
||||||
|
when "pw"
|
||||||
|
write_perm_acl = ["*"]
|
||||||
|
when "prw"
|
||||||
|
read_perm_acl = [".r:*",".rlistings"]
|
||||||
|
write_perm_acl = ["*"]
|
||||||
|
end
|
||||||
|
elsif valid_account_perms.include?(perm)
|
||||||
|
# tackle the user access differently
|
||||||
|
unless (users.nil? || users.empty?)
|
||||||
|
# return the correct acls
|
||||||
|
tenant_id = "*" # this might change later
|
||||||
|
acl_array = users.map { |u| "#{tenant_id}:#{u}" }
|
||||||
|
#acl_string = acl_array.join(',')
|
||||||
|
case perm
|
||||||
|
when "r"
|
||||||
|
read_perm_acl = acl_array
|
||||||
|
when "w"
|
||||||
|
write_perm_acl = acl_array
|
||||||
|
when "rw"
|
||||||
|
read_perm_acl = acl_array
|
||||||
|
write_perm_acl = acl_array
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return read_perm_acl, write_perm_acl
|
||||||
|
end
|
||||||
|
|
||||||
|
def perm_acl_to_header(read_perm_acl, write_perm_acl)
|
||||||
header = {}
|
header = {}
|
||||||
case acl
|
if read_perm_acl.nil? && write_perm_acl.nil?
|
||||||
when "private"
|
header = {'X-Container-Read' => "", 'X-Container-Write' => ""}
|
||||||
header['X-Container-Read'] = ""
|
elsif !read_perm_acl.nil? && write_perm_acl.nil?
|
||||||
header['X-Container-Write'] = ""
|
header = {'X-Container-Read' => "#{read_perm_acl.join(',')}", 'X-Container-Write' => ""}
|
||||||
when "public-read"
|
elsif read_perm_acl.nil? && !write_perm_acl.nil?
|
||||||
header['X-Container-Read'] = ".r:*,.rlistings"
|
header = {'X-Container-Read' => "", 'X-Container-Write' => "#{write_perm_acl.join(',')}"}
|
||||||
when "public-write"
|
elsif !read_perm_acl.nil? && !write_perm_acl.nil?
|
||||||
header['X-Container-Write'] = "*"
|
header = {'X-Container-Read' => "#{read_perm_acl.join(',')}", 'X-Container-Write' => "#{write_perm_acl.join(',')}"}
|
||||||
when "public-read-write"
|
|
||||||
header['X-Container-Read'] = ".r:*,.rlistings"
|
|
||||||
header['X-Container-Write'] = "*"
|
|
||||||
end
|
end
|
||||||
header
|
header
|
||||||
end
|
end
|
||||||
|
|
||||||
def header_to_acl(read_header=nil, write_header=nil)
|
def header_to_perm_acl(read_header=nil, write_header=nil)
|
||||||
acl = nil
|
read_h, write_h = nil
|
||||||
if read_header.nil? && write_header.nil?
|
read_h = read_header.split(',') unless read_header.nil?
|
||||||
acl = nil
|
write_h = write_header.split(',') unless write_header.nil?
|
||||||
elsif !read_header.nil? && read_header.include?(".r:*") && write_header.nil?
|
return read_h, write_h
|
||||||
acl = "public-read"
|
|
||||||
elsif !write_header.nil? && write_header.include?("*") && read_header.nil?
|
|
||||||
acl = "public-write"
|
|
||||||
elsif !read_header.nil? && read_header.include?(".r:*") && !write_header.nil? && write_header.include?("*")
|
|
||||||
acl = "public-read-write"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_object_temp_url(container, object, expires_secs, method)
|
def generate_object_temp_url(container, object, expires_secs, method)
|
||||||
|
@ -230,6 +285,37 @@ module Fog
|
||||||
response
|
response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# this request is used only for get_shared_container and get_shared_object calls
|
||||||
|
def shared_request(params, parse_json = true, &block)
|
||||||
|
begin
|
||||||
|
response = @connection.request(params.merge!({
|
||||||
|
:headers => {
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
'X-Auth-Token' => @auth_token
|
||||||
|
}.merge!(params[:headers] || {}),
|
||||||
|
:host => @host,
|
||||||
|
:path => "#{params[:path]}",
|
||||||
|
}), &block)
|
||||||
|
rescue Excon::Errors::HTTPStatusError => error
|
||||||
|
raise case error
|
||||||
|
when Excon::Errors::NotFound
|
||||||
|
Fog::Storage::HP::NotFound.slurp(error)
|
||||||
|
when Excon::Errors::Forbidden
|
||||||
|
Fog::HP::Errors::Forbidden.slurp(error)
|
||||||
|
else
|
||||||
|
error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if !response.body.empty? && parse_json && response.headers['Content-Type'] =~ %r{application/json}
|
||||||
|
begin
|
||||||
|
response.body = MultiJson.decode(response.body)
|
||||||
|
rescue MultiJson::DecodeError => error
|
||||||
|
response.body #### the body is not in JSON format so just return it as it is
|
||||||
|
end
|
||||||
|
end
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue