commit
c29e681a40
3
Gemfile
3
Gemfile
@ -5,6 +5,8 @@ gem "sinatra-contrib"
|
|||||||
gem "activesupport", '~> 3.2'
|
gem "activesupport", '~> 3.2'
|
||||||
gem "riak-client", :github => "5apps/riak-ruby-client", :branch => "invalid_uri_error"
|
gem "riak-client", :github => "5apps/riak-ruby-client", :branch => "invalid_uri_error"
|
||||||
gem "fog"
|
gem "fog"
|
||||||
|
gem "rest-client"
|
||||||
|
gem "redis"
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'rake'
|
gem 'rake'
|
||||||
@ -14,4 +16,5 @@ end
|
|||||||
|
|
||||||
group :staging, :production do
|
group :staging, :production do
|
||||||
gem "rainbows"
|
gem "rainbows"
|
||||||
|
gem "sentry-raven"
|
||||||
end
|
end
|
||||||
|
11
Gemfile.lock
11
Gemfile.lock
@ -21,6 +21,8 @@ GEM
|
|||||||
builder (3.2.2)
|
builder (3.2.2)
|
||||||
eventmachine (1.0.3)
|
eventmachine (1.0.3)
|
||||||
excon (0.16.10)
|
excon (0.16.10)
|
||||||
|
faraday (0.9.1)
|
||||||
|
multipart-post (>= 1.2, < 3)
|
||||||
fog (1.7.0)
|
fog (1.7.0)
|
||||||
builder
|
builder
|
||||||
excon (~> 0.14)
|
excon (~> 0.14)
|
||||||
@ -42,6 +44,7 @@ GEM
|
|||||||
mime-types (1.23)
|
mime-types (1.23)
|
||||||
minitest (2.10.0)
|
minitest (2.10.0)
|
||||||
multi_json (1.10.0)
|
multi_json (1.10.0)
|
||||||
|
multipart-post (2.0.0)
|
||||||
net-scp (1.0.4)
|
net-scp (1.0.4)
|
||||||
net-ssh (>= 1.99.1)
|
net-ssh (>= 1.99.1)
|
||||||
net-ssh (2.6.7)
|
net-ssh (2.6.7)
|
||||||
@ -59,7 +62,12 @@ GEM
|
|||||||
unicorn (~> 4.8)
|
unicorn (~> 4.8)
|
||||||
raindrops (0.13.0)
|
raindrops (0.13.0)
|
||||||
rake (0.9.2.2)
|
rake (0.9.2.2)
|
||||||
|
redis (3.0.7)
|
||||||
|
rest-client (1.6.7)
|
||||||
|
mime-types (>= 1.16)
|
||||||
ruby-hmac (0.4.0)
|
ruby-hmac (0.4.0)
|
||||||
|
sentry-raven (0.13.1)
|
||||||
|
faraday (>= 0.7.6)
|
||||||
sinatra (1.4.5)
|
sinatra (1.4.5)
|
||||||
rack (~> 1.4)
|
rack (~> 1.4)
|
||||||
rack-protection (~> 1.4)
|
rack-protection (~> 1.4)
|
||||||
@ -87,6 +95,9 @@ DEPENDENCIES
|
|||||||
purdytest
|
purdytest
|
||||||
rainbows
|
rainbows
|
||||||
rake
|
rake
|
||||||
|
redis
|
||||||
|
rest-client
|
||||||
riak-client!
|
riak-client!
|
||||||
|
sentry-raven
|
||||||
sinatra (~> 1.4)
|
sinatra (~> 1.4)
|
||||||
sinatra-contrib
|
sinatra-contrib
|
||||||
|
@ -13,6 +13,13 @@ development: &defaults
|
|||||||
cs_binaries: rs.binaries
|
cs_binaries: rs.binaries
|
||||||
authorizations: rs_authorizations
|
authorizations: rs_authorizations
|
||||||
opslog: rs_opslog
|
opslog: rs_opslog
|
||||||
|
# # uncomment this section and comment the riak one
|
||||||
|
# swift: &swift_defaults
|
||||||
|
# host: "https://swift.example.com"
|
||||||
|
# # Redis is needed for the swift backend
|
||||||
|
# redis:
|
||||||
|
# host: localhost
|
||||||
|
# port: 6379
|
||||||
|
|
||||||
test:
|
test:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
|
@ -15,7 +15,7 @@ module RemoteStorage
|
|||||||
self.settings = settings
|
self.settings = settings
|
||||||
self.server = server
|
self.server = server
|
||||||
|
|
||||||
credentials = File.read(settings['riak_cs']['credentials_file'])
|
credentials = File.read(settings.riak['riak_cs']['credentials_file'])
|
||||||
self.cs_credentials = JSON.parse(credentials)
|
self.cs_credentials = JSON.parse(credentials)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -229,8 +229,7 @@ module RemoteStorage
|
|||||||
|
|
||||||
# A URI object that can be used with HTTP backend methods
|
# A URI object that can be used with HTTP backend methods
|
||||||
def riak_uri(bucket, key)
|
def riak_uri(bucket, key)
|
||||||
rc = settings.symbolize_keys
|
URI.parse "http://#{settings.riak["host"]}:#{settings.riak["http_port"]}/riak/#{bucket}/#{key}"
|
||||||
URI.parse "http://#{rc[:host]}:#{rc[:http_port]}/riak/#{bucket}/#{key}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def serializer_for(content_type)
|
def serializer_for(content_type)
|
||||||
@ -471,13 +470,13 @@ module RemoteStorage
|
|||||||
end
|
end
|
||||||
|
|
||||||
def client
|
def client
|
||||||
@client ||= ::Riak::Client.new(:host => settings['host'],
|
@client ||= ::Riak::Client.new(:host => settings.riak['host'],
|
||||||
:http_port => settings['http_port'])
|
:http_port => settings.riak['http_port'])
|
||||||
end
|
end
|
||||||
|
|
||||||
def data_bucket
|
def data_bucket
|
||||||
@data_bucket ||= begin
|
@data_bucket ||= begin
|
||||||
bucket = client.bucket(settings['buckets']['data'])
|
bucket = client.bucket(settings.riak['buckets']['data'])
|
||||||
bucket.allow_mult = false
|
bucket.allow_mult = false
|
||||||
bucket
|
bucket
|
||||||
end
|
end
|
||||||
@ -485,7 +484,7 @@ module RemoteStorage
|
|||||||
|
|
||||||
def directory_bucket
|
def directory_bucket
|
||||||
@directory_bucket ||= begin
|
@directory_bucket ||= begin
|
||||||
bucket = client.bucket(settings['buckets']['directories'])
|
bucket = client.bucket(settings.riak['buckets']['directories'])
|
||||||
bucket.allow_mult = false
|
bucket.allow_mult = false
|
||||||
bucket
|
bucket
|
||||||
end
|
end
|
||||||
@ -493,7 +492,7 @@ module RemoteStorage
|
|||||||
|
|
||||||
def auth_bucket
|
def auth_bucket
|
||||||
@auth_bucket ||= begin
|
@auth_bucket ||= begin
|
||||||
bucket = client.bucket(settings['buckets']['authorizations'])
|
bucket = client.bucket(settings.riak['buckets']['authorizations'])
|
||||||
bucket.allow_mult = false
|
bucket.allow_mult = false
|
||||||
bucket
|
bucket
|
||||||
end
|
end
|
||||||
@ -501,7 +500,7 @@ module RemoteStorage
|
|||||||
|
|
||||||
def binary_bucket
|
def binary_bucket
|
||||||
@binary_bucket ||= begin
|
@binary_bucket ||= begin
|
||||||
bucket = client.bucket(settings['buckets']['binaries'])
|
bucket = client.bucket(settings.riak['buckets']['binaries'])
|
||||||
bucket.allow_mult = false
|
bucket.allow_mult = false
|
||||||
bucket
|
bucket
|
||||||
end
|
end
|
||||||
@ -509,7 +508,7 @@ module RemoteStorage
|
|||||||
|
|
||||||
def opslog_bucket
|
def opslog_bucket
|
||||||
@opslog_bucket ||= begin
|
@opslog_bucket ||= begin
|
||||||
bucket = client.bucket(settings['buckets']['opslog'])
|
bucket = client.bucket(settings.riak['buckets']['opslog'])
|
||||||
bucket.allow_mult = false
|
bucket.allow_mult = false
|
||||||
bucket
|
bucket
|
||||||
end
|
end
|
||||||
@ -520,12 +519,12 @@ module RemoteStorage
|
|||||||
:provider => 'AWS',
|
:provider => 'AWS',
|
||||||
:aws_access_key_id => cs_credentials['key_id'],
|
:aws_access_key_id => cs_credentials['key_id'],
|
||||||
:aws_secret_access_key => cs_credentials['key_secret'],
|
:aws_secret_access_key => cs_credentials['key_secret'],
|
||||||
:endpoint => settings['riak_cs']['endpoint']
|
:endpoint => settings.riak['riak_cs']['endpoint']
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def cs_binary_bucket
|
def cs_binary_bucket
|
||||||
@cs_binary_bucket ||= cs_client.directories.create(:key => settings['buckets']['cs_binaries'])
|
@cs_binary_bucket ||= cs_client.directories.create(:key => settings.riak['buckets']['cs_binaries'])
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
311
lib/remote_storage/swift.rb
Normal file
311
lib/remote_storage/swift.rb
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
require "rest_client"
|
||||||
|
require "json"
|
||||||
|
require "cgi"
|
||||||
|
require "active_support/core_ext/time/conversions"
|
||||||
|
require "active_support/core_ext/numeric/time"
|
||||||
|
require "redis"
|
||||||
|
|
||||||
|
module RemoteStorage
|
||||||
|
class Swift
|
||||||
|
|
||||||
|
attr_accessor :settings, :server
|
||||||
|
|
||||||
|
def initialize(settings, server)
|
||||||
|
self.settings = settings
|
||||||
|
self.server = server
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorize_request(user, directory, token, listing=false)
|
||||||
|
request_method = server.env["REQUEST_METHOD"]
|
||||||
|
|
||||||
|
if directory.split("/").first == "public"
|
||||||
|
return true if ["GET", "HEAD"].include?(request_method) && !listing
|
||||||
|
end
|
||||||
|
|
||||||
|
authorizations = redis.smembers("authorizations:#{user}:#{token}")
|
||||||
|
permission = directory_permission(authorizations, directory)
|
||||||
|
|
||||||
|
server.halt 401 unless permission
|
||||||
|
if ["PUT", "DELETE"].include? request_method
|
||||||
|
server.halt 401 unless permission == "rw"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_head(user, directory, key)
|
||||||
|
url = url_for(user, directory, key)
|
||||||
|
|
||||||
|
res = do_head_request(url)
|
||||||
|
|
||||||
|
set_response_headers(res)
|
||||||
|
rescue RestClient::ResourceNotFound
|
||||||
|
server.halt 404
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_data(user, directory, key)
|
||||||
|
url = url_for(user, directory, key)
|
||||||
|
|
||||||
|
res = do_get_request(url)
|
||||||
|
|
||||||
|
set_response_headers(res)
|
||||||
|
|
||||||
|
none_match = (server.env["HTTP_IF_NONE_MATCH"] || "").split(",").map(&:strip)
|
||||||
|
server.halt 304 if none_match.include? %Q("#{res.headers[:etag]}")
|
||||||
|
|
||||||
|
return res.body
|
||||||
|
rescue RestClient::ResourceNotFound
|
||||||
|
server.halt 404
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_head_directory_listing(user, directory)
|
||||||
|
res = do_head_request("#{container_url_for(user)}/#{directory}/")
|
||||||
|
|
||||||
|
server.headers["Content-Type"] = "application/json"
|
||||||
|
server.headers["ETag"] = %Q("#{res.headers[:etag]}")
|
||||||
|
rescue RestClient::ResourceNotFound
|
||||||
|
server.halt 404
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_directory_listing(user, directory)
|
||||||
|
server.headers["Content-Type"] = "application/json"
|
||||||
|
|
||||||
|
do_head_request("#{container_url_for(user)}/#{directory}/") do |response|
|
||||||
|
if response.code == 404
|
||||||
|
return directory_listing([]).to_json
|
||||||
|
else
|
||||||
|
server.headers["ETag"] = %Q("#{response.headers[:etag]}")
|
||||||
|
none_match = (server.env["HTTP_IF_NONE_MATCH"] || "").split(",").map(&:strip)
|
||||||
|
server.halt 304 if none_match.include? %Q("#{response.headers[:etag]}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
res = do_get_request("#{container_url_for(user)}/?format=json&path=#{directory}/")
|
||||||
|
|
||||||
|
if body = JSON.parse(res.body)
|
||||||
|
listing = directory_listing(body)
|
||||||
|
else
|
||||||
|
puts "listing not JSON"
|
||||||
|
end
|
||||||
|
|
||||||
|
listing.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_data(user, directory, key, data, content_type)
|
||||||
|
server.halt 409 if has_name_collision?(user, directory, key)
|
||||||
|
|
||||||
|
url = url_for(user, directory, key)
|
||||||
|
|
||||||
|
if required_match = server.env["HTTP_IF_MATCH"]
|
||||||
|
do_head_request(url) do |response|
|
||||||
|
server.halt 412 unless required_match == %Q("#{response.headers[:etag]}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if server.env["HTTP_IF_NONE_MATCH"] == "*"
|
||||||
|
do_head_request(url) do |response|
|
||||||
|
server.halt 412 unless response.code == 404
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
res = do_put_request(url, data, content_type)
|
||||||
|
|
||||||
|
if update_dir_objects(user, directory)
|
||||||
|
server.headers["ETag"] = %Q("#{res.headers[:etag]}")
|
||||||
|
server.halt 200
|
||||||
|
else
|
||||||
|
server.halt 500
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_data(user, directory, key)
|
||||||
|
url = url_for(user, directory, key)
|
||||||
|
|
||||||
|
if required_match = server.env["HTTP_IF_MATCH"]
|
||||||
|
do_head_request(url) do |response|
|
||||||
|
server.halt 412 unless required_match == %Q("#{response.headers[:etag]}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
do_delete_request(url)
|
||||||
|
delete_dir_objects(user, directory)
|
||||||
|
|
||||||
|
server.halt 200
|
||||||
|
rescue RestClient::ResourceNotFound
|
||||||
|
server.halt 404
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_response_headers(response)
|
||||||
|
server.headers["ETag"] = %Q("#{response.headers[:etag]}")
|
||||||
|
server.headers["Content-Type"] = response.headers[:content_type]
|
||||||
|
server.headers["Content-Length"] = response.headers[:content_length]
|
||||||
|
server.headers["Last-Modified"] = response.headers[:last_modified]
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_category(directory)
|
||||||
|
if directory.match(/^public\//)
|
||||||
|
"public/#{directory.split('/')[1]}"
|
||||||
|
else
|
||||||
|
directory.split('/').first
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def directory_permission(authorizations, directory)
|
||||||
|
authorizations = authorizations.map do |auth|
|
||||||
|
auth.index(":") ? auth.split(":") : [auth, "rw"]
|
||||||
|
end
|
||||||
|
authorizations = Hash[*authorizations.flatten]
|
||||||
|
|
||||||
|
permission = authorizations[""]
|
||||||
|
|
||||||
|
authorizations.each do |key, value|
|
||||||
|
if directory.match(/^(public\/)?#{key}(\/|$)/)
|
||||||
|
if permission.nil? || permission == "r"
|
||||||
|
permission = value
|
||||||
|
end
|
||||||
|
return permission if permission == "rw"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
permission
|
||||||
|
end
|
||||||
|
|
||||||
|
def directory_listing(res_body)
|
||||||
|
listing = {
|
||||||
|
"@context" => "http://remotestorage.io/spec/folder-description",
|
||||||
|
"items" => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
res_body.each do |entry|
|
||||||
|
name = entry["name"].gsub("#{File.dirname(entry["name"])}/", '')
|
||||||
|
if name[-1] == "/"
|
||||||
|
listing["items"].merge!({
|
||||||
|
name => {
|
||||||
|
"ETag" => entry["hash"],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
else
|
||||||
|
listing["items"].merge!({
|
||||||
|
name => {
|
||||||
|
"ETag" => entry["hash"],
|
||||||
|
"Content-Type" => entry["content_type"],
|
||||||
|
"Content-Length" => entry["bytes"]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
listing
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_name_collision?(user, directory, key)
|
||||||
|
# check for existing directory with the same name as the document
|
||||||
|
do_head_request("#{container_url_for(user)}/#{directory}/#{key}/") do |res|
|
||||||
|
return true if res.code == 200
|
||||||
|
end
|
||||||
|
|
||||||
|
# check for existing documents with the same name as one of the parent directories
|
||||||
|
parent_directories_for(directory).each do |dir|
|
||||||
|
do_head_request("#{container_url_for(user)}/#{dir}/") do |res_dir|
|
||||||
|
if res_dir.code == 200
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
do_head_request("#{container_url_for(user)}/#{dir}") do |res_key|
|
||||||
|
if res_key.code == 200
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def parent_directories_for(directory)
|
||||||
|
directories = directory.split("/")
|
||||||
|
parent_directories = []
|
||||||
|
|
||||||
|
while directories.any?
|
||||||
|
parent_directories << directories.join("/")
|
||||||
|
directories.pop
|
||||||
|
end
|
||||||
|
|
||||||
|
parent_directories
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_dir_objects(user, directory)
|
||||||
|
timestamp = (Time.now.to_f * 1000).to_i
|
||||||
|
|
||||||
|
parent_directories_for(directory).each do |dir|
|
||||||
|
do_put_request("#{container_url_for(user)}/#{dir}/", timestamp.to_s, "text/plain")
|
||||||
|
end
|
||||||
|
|
||||||
|
true
|
||||||
|
rescue
|
||||||
|
parent_directories_for(directory).each do |dir|
|
||||||
|
do_delete_request("#{container_url_for(user)}/#{dir}/") rescue false
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_dir_objects(user, directory)
|
||||||
|
parent_directories_for(directory).each do |dir|
|
||||||
|
if dir_empty?(user, dir)
|
||||||
|
do_delete_request("#{container_url_for(user)}/#{dir}/")
|
||||||
|
else
|
||||||
|
timestamp = (Time.now.to_f * 1000).to_i
|
||||||
|
do_put_request("#{container_url_for(user)}/#{dir}/", timestamp.to_s, "text/plain")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def dir_empty?(user, dir)
|
||||||
|
do_get_request("#{container_url_for(user)}/?format=plain&limit=1&path=#{dir}/") do |res|
|
||||||
|
return res.headers[:content_length] == "0"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def container_url_for(user)
|
||||||
|
"#{base_url}/#{container_for(user)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def url_for(user, directory, key)
|
||||||
|
"#{container_url_for(user)}/#{directory}/#{key}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def base_url
|
||||||
|
@base_url ||= settings.swift["host"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def container_for(user)
|
||||||
|
"rs:#{settings.environment.to_s.chars.first}:#{user}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_headers
|
||||||
|
@default_headers ||= {"x-auth-token" => settings.swift["token"]}
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_put_request(url, data, content_type)
|
||||||
|
RestClient.put(url, data, default_headers.merge({content_type: content_type}))
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_get_request(url, &block)
|
||||||
|
RestClient.get(url, default_headers, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_head_request(url, &block)
|
||||||
|
RestClient.head(url, default_headers, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def do_delete_request(url)
|
||||||
|
RestClient.delete(url, default_headers)
|
||||||
|
end
|
||||||
|
|
||||||
|
def redis
|
||||||
|
@redis ||= Redis.new(host: settings.redis["host"], port: settings.redis["port"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -5,6 +5,7 @@ require "sinatra/base"
|
|||||||
require 'sinatra/config_file'
|
require 'sinatra/config_file'
|
||||||
require "sinatra/reloader"
|
require "sinatra/reloader"
|
||||||
require "remote_storage/riak"
|
require "remote_storage/riak"
|
||||||
|
require "remote_storage/swift"
|
||||||
|
|
||||||
class LiquorCabinet < Sinatra::Base
|
class LiquorCabinet < Sinatra::Base
|
||||||
|
|
||||||
@ -23,11 +24,22 @@ class LiquorCabinet < Sinatra::Base
|
|||||||
|
|
||||||
configure :development do
|
configure :development do
|
||||||
register Sinatra::Reloader
|
register Sinatra::Reloader
|
||||||
|
also_reload "lib/remote_storage/*.rb"
|
||||||
enable :logging
|
enable :logging
|
||||||
end
|
end
|
||||||
|
|
||||||
configure :production, :staging do
|
configure :production, :staging do
|
||||||
require "rack/common_logger"
|
require "rack/common_logger"
|
||||||
|
if ENV['SENTRY_DSN']
|
||||||
|
require "raven"
|
||||||
|
|
||||||
|
Raven.configure do |config|
|
||||||
|
config.dsn = ENV['SENTRY_DSN']
|
||||||
|
config.tags = { environment: settings.environment.to_s }
|
||||||
|
end
|
||||||
|
|
||||||
|
use Raven::Rack
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -62,17 +74,19 @@ class LiquorCabinet < Sinatra::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
["/:user/*/:key", "/:user/:key"].each do |path|
|
["/:user/*/:key", "/:user/:key"].each do |path|
|
||||||
get path do
|
|
||||||
storage.get_data(@user, @directory, @key)
|
|
||||||
end
|
|
||||||
|
|
||||||
head path do
|
head path do
|
||||||
storage.get_head(@user, @directory, @key)
|
storage.get_head(@user, @directory, @key)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
get path do
|
||||||
|
storage.get_data(@user, @directory, @key)
|
||||||
|
end
|
||||||
|
|
||||||
put path do
|
put path do
|
||||||
data = request.body.read
|
data = request.body.read
|
||||||
|
|
||||||
|
halt 422 unless env['CONTENT_TYPE']
|
||||||
|
|
||||||
if env['CONTENT_TYPE'] == "application/x-www-form-urlencoded"
|
if env['CONTENT_TYPE'] == "application/x-www-form-urlencoded"
|
||||||
content_type = "text/plain; charset=utf-8"
|
content_type = "text/plain; charset=utf-8"
|
||||||
else
|
else
|
||||||
@ -88,23 +102,29 @@ class LiquorCabinet < Sinatra::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
["/:user/*/", "/:user/"].each do |path|
|
["/:user/*/", "/:user/"].each do |path|
|
||||||
get path do
|
|
||||||
storage.get_directory_listing(@user, @directory)
|
|
||||||
end
|
|
||||||
|
|
||||||
head path do
|
head path do
|
||||||
storage.get_head_directory_listing(@user, @directory)
|
storage.get_head_directory_listing(@user, @directory)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
get path do
|
||||||
|
storage.get_directory_listing(@user, @directory)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def storage
|
def storage
|
||||||
@storage ||= begin
|
@storage ||= begin
|
||||||
if settings.riak
|
if settings.respond_to? :riak
|
||||||
RemoteStorage::Riak.new(settings.riak, self)
|
RemoteStorage::Riak.new(settings, self)
|
||||||
# elsif settings.redis
|
elsif settings.respond_to? :swift
|
||||||
# include RemoteStorage::Redis
|
settings.swift["token"] = File.read("tmp/swift_token.txt")
|
||||||
|
RemoteStorage::Swift.new(settings, self)
|
||||||
|
else
|
||||||
|
puts <<-EOF
|
||||||
|
You need to set one storage backend in your config.yml file.
|
||||||
|
Riak and Swift are currently supported. See config.yml.example.
|
||||||
|
EOF
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -17,19 +17,11 @@ describe "App with Riak backend" do
|
|||||||
head "/jimmy/public/foo"
|
head "/jimmy/public/foo"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns an empty body" do
|
it "works" do
|
||||||
last_response.status.must_equal 200
|
last_response.status.must_equal 200
|
||||||
last_response.body.must_equal ""
|
last_response.body.must_equal ""
|
||||||
end
|
|
||||||
|
|
||||||
it "has an ETag header set" do
|
|
||||||
last_response.status.must_equal 200
|
|
||||||
last_response.headers["ETag"].wont_be_nil
|
last_response.headers["ETag"].wont_be_nil
|
||||||
end
|
last_response.headers["Content-Length"].must_equal "14"
|
||||||
|
|
||||||
it "has a Content-Length header set" do
|
|
||||||
last_response.status.must_equal 200
|
|
||||||
last_response.headers["Content-Length"].must_equal '14'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -44,26 +36,13 @@ describe "App with Riak backend" do
|
|||||||
get "/jimmy/public/foo"
|
get "/jimmy/public/foo"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns the value on all get requests" do
|
it "works" do
|
||||||
last_response.status.must_equal 200
|
last_response.status.must_equal 200
|
||||||
last_response.body.must_equal "some text data"
|
last_response.body.must_equal "some text data"
|
||||||
end
|
|
||||||
|
|
||||||
it "has an ETag header set" do
|
|
||||||
last_response.status.must_equal 200
|
|
||||||
last_response.headers["ETag"].wont_be_nil
|
last_response.headers["ETag"].wont_be_nil
|
||||||
end
|
|
||||||
|
|
||||||
it "has a Content-Length header set" do
|
|
||||||
last_response.status.must_equal 200
|
|
||||||
last_response.headers["Content-Length"].must_equal "14"
|
last_response.headers["Content-Length"].must_equal "14"
|
||||||
end
|
|
||||||
|
|
||||||
it "has caching headers set" do
|
|
||||||
last_response.status.must_equal 200
|
|
||||||
last_response.headers["Expires"].must_equal "0"
|
last_response.headers["Expires"].must_equal "0"
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
describe "empty file" do
|
describe "empty file" do
|
||||||
before do
|
before do
|
||||||
@ -124,19 +103,11 @@ describe "App with Riak backend" do
|
|||||||
head "/jimmy/documents/foo"
|
head "/jimmy/documents/foo"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns an empty body" do
|
it "works" do
|
||||||
last_response.status.must_equal 200
|
last_response.status.must_equal 200
|
||||||
last_response.body.must_equal ""
|
last_response.body.must_equal ""
|
||||||
end
|
|
||||||
|
|
||||||
it "has an ETag header set" do
|
|
||||||
last_response.status.must_equal 200
|
|
||||||
last_response.headers["ETag"].wont_be_nil
|
last_response.headers["ETag"].wont_be_nil
|
||||||
end
|
last_response.headers["Content-Length"].must_equal "22"
|
||||||
|
|
||||||
it "has a Content-Length header set" do
|
|
||||||
last_response.status.must_equal 200
|
|
||||||
last_response.headers["Content-Length"].must_equal '22'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -325,7 +296,7 @@ describe "App with Riak backend" do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "naming collissions between documents and directories" do
|
describe "naming collisions between documents and directories" do
|
||||||
before do
|
before do
|
||||||
put "/jimmy/documents/archive/document", "lorem ipsum"
|
put "/jimmy/documents/archive/document", "lorem ipsum"
|
||||||
end
|
end
|
||||||
@ -801,3 +772,4 @@ describe "App with Riak backend" do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
@ -28,8 +28,7 @@ def write_last_response_to_file(filename = "last_response.html")
|
|||||||
end
|
end
|
||||||
|
|
||||||
alias context describe
|
alias context describe
|
||||||
|
if app.settings.respond_to? :riak
|
||||||
if app.settings.riak
|
|
||||||
::Riak.disable_list_keys_warnings = true
|
::Riak.disable_list_keys_warnings = true
|
||||||
|
|
||||||
def client
|
def client
|
||||||
|
Loading…
x
Reference in New Issue
Block a user