vbucket 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +17 -14
- data/Gemfile.lock +59 -0
- data/LICENSE.txt +22 -0
- data/README.md +58 -18
- data/Rakefile +7 -0
- data/bin/vbucket +5 -0
- data/config.ru +3 -0
- data/config/vbucket.conf +3 -0
- data/config/vbucket.keys +5 -0
- data/lib/vbucket.rb +24 -3
- data/lib/vbucket/configuration.rb +32 -0
- data/lib/vbucket/service.rb +146 -0
- data/lib/vbucket/version.rb +1 -1
- data/log/.gitignore +4 -0
- data/spec/assets/cat.jpg +0 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/unit/vbucket/configuration_spec.rb +41 -0
- data/spec/unit/vbucket/service_spec.rb +134 -0
- data/vbucket.gemspec +17 -10
- metadata +102 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8596592bde62eac7f7d9c6cb19be5587c84975bc
|
4
|
+
data.tar.gz: aa5038802b098ec31ee74233b13548521a8f3e1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f9d9da0ed244628d0ca7ec18695cf9610eba9e88be2b2c113725a9ddfab2741091b3136fcfb781061c44c8d364bafcb4b46e8b6551db6bf4cf1fafe462251a53
|
7
|
+
data.tar.gz: d74aa0ba522152dfa4865b43d091bfa69fddd0e908c8329d607e813ec9693d827bdefc9ad81298448792eaa550c1cbd0e810d09faa4a780e313197a90355b5a8
|
data/.gitignore
CHANGED
@@ -1,14 +1,17 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
/
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
InstalledFiles
|
7
|
+
_yardoc
|
8
|
+
coverage
|
9
|
+
doc/
|
10
|
+
lib/bundler/man
|
11
|
+
pkg
|
12
|
+
rdoc
|
13
|
+
spec/reports
|
14
|
+
test/tmp
|
15
|
+
test/version_tmp
|
16
|
+
tmp
|
17
|
+
.idea/
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
vbucket (0.1.0)
|
5
|
+
nokogiri
|
6
|
+
rack-ssl
|
7
|
+
sinatra
|
8
|
+
sinatra-contrib
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
backports (3.6.4)
|
14
|
+
diff-lcs (1.2.5)
|
15
|
+
mini_portile (0.6.1)
|
16
|
+
multi_json (1.10.1)
|
17
|
+
nokogiri (1.6.5)
|
18
|
+
mini_portile (~> 0.6.0)
|
19
|
+
rack (1.6.0)
|
20
|
+
rack-protection (1.5.3)
|
21
|
+
rack
|
22
|
+
rack-ssl (1.4.1)
|
23
|
+
rack
|
24
|
+
rack-test (0.6.2)
|
25
|
+
rack (>= 1.0)
|
26
|
+
rake (10.3.2)
|
27
|
+
rspec (3.1.0)
|
28
|
+
rspec-core (~> 3.1.0)
|
29
|
+
rspec-expectations (~> 3.1.0)
|
30
|
+
rspec-mocks (~> 3.1.0)
|
31
|
+
rspec-core (3.1.2)
|
32
|
+
rspec-support (~> 3.1.0)
|
33
|
+
rspec-expectations (3.1.0)
|
34
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
35
|
+
rspec-support (~> 3.1.0)
|
36
|
+
rspec-mocks (3.1.0)
|
37
|
+
rspec-support (~> 3.1.0)
|
38
|
+
rspec-support (3.1.0)
|
39
|
+
sinatra (1.4.5)
|
40
|
+
rack (~> 1.4)
|
41
|
+
rack-protection (~> 1.4)
|
42
|
+
tilt (~> 1.3, >= 1.3.4)
|
43
|
+
sinatra-contrib (1.4.2)
|
44
|
+
backports (>= 2.0)
|
45
|
+
multi_json
|
46
|
+
rack-protection
|
47
|
+
rack-test
|
48
|
+
sinatra (~> 1.4.0)
|
49
|
+
tilt (~> 1.3)
|
50
|
+
tilt (1.4.1)
|
51
|
+
|
52
|
+
PLATFORMS
|
53
|
+
ruby
|
54
|
+
|
55
|
+
DEPENDENCIES
|
56
|
+
bundler (~> 1.3)
|
57
|
+
rake
|
58
|
+
rspec
|
59
|
+
vbucket!
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 jakerobinson
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,31 +1,71 @@
|
|
1
|
-
#
|
1
|
+
# vBucket
|
2
2
|
|
3
|
-
|
3
|
+
## What is vBucket?
|
4
|
+
vBucket is an API for your files.
|
4
5
|
|
5
|
-
|
6
|
+
It's goal is to provide you with http access to a directory on your linux filesystem and any metadata you want to add.
|
6
7
|
|
7
|
-
|
8
|
+
You can GET, POST, PUT and DELETE with a simple curl command:
|
9
|
+
```
|
10
|
+
# POST a file
|
11
|
+
curl -i -F file=@funny_cat.gif http://vbucket.example.com/
|
12
|
+
|
13
|
+
# GET the file
|
14
|
+
curl http://vbucket.example.com/funny_cat.gif
|
8
15
|
|
9
|
-
|
10
|
-
|
16
|
+
# DELETE the file
|
17
|
+
curl -X DELETE http://vbucket.example.com/funny_cat.gif
|
18
|
+
|
19
|
+
#PUT a file
|
20
|
+
curl --upload-file another_cat.gif http://vbucket.example.com/another_cat.gif
|
11
21
|
```
|
12
22
|
|
13
|
-
|
23
|
+
## vBucket vs.
|
24
|
+
### object storage
|
25
|
+
Object storage stores data as objects with metadata, identified by a uuid. An API is employed to access the objects.
|
26
|
+
|
27
|
+
vBucket is a REST API for a block storage filesystem. It has all the power of an object storage API without
|
28
|
+
the need for the underlying object storage hardware.
|
29
|
+
|
30
|
+
### webdav
|
31
|
+
WebDAV(Web Distributed Authoring and Versioning) is a unique protocol with many features for reading and writing documents.
|
32
|
+
|
33
|
+
vBucket uses HTTP for all it's communication, providing maximum accessibility to any language or system. POST, PUT, GET, and DELETE are the only methods you need to know.
|
34
|
+
|
35
|
+
### nfs
|
36
|
+
NFS(Network File System) is a distributed, open filesystem protocol that you would mount and treat like a local disk.
|
37
|
+
It is extremely valuable if you need complete control over the files and their heirarchy.
|
38
|
+
|
39
|
+
vBucket runs over HTTP, so there is no mounting or special ports to open. It works through firewalls, proxies, and NAT.
|
40
|
+
|
41
|
+
### iscsi
|
42
|
+
iSCSI(Internet Small Computer System Interface) is an IP protocol for sending SCSI commands over the network. You can do everything from mount a disk to boot from iSCSI. It is an extremely powerful storage bus.
|
43
|
+
|
44
|
+
vBucket is very lightweight and easy to install. The only requirements to get started are Linux and Ruby.
|
45
|
+
|
46
|
+
## Security
|
47
|
+
vBucket does not come with an encryption or authentication mechanism. If you plan on running vBucket over the Internet, its very simple to host behind Apache or Nginx. These web servers can provide basic authentication, SSL, reverse proxy.
|
48
|
+
|
49
|
+
## Applications
|
50
|
+
|
51
|
+
### File Server
|
52
|
+
At it's core, vBucket is designed to serve up files. If you want to host files that are accessible via REST API, you can simply run vBucket as a service and point it at a directory on your linux system. You can upload and download files over HTTP with a multitude of languages, libraries and tools.
|
14
53
|
|
15
|
-
|
54
|
+
### YUM/APT Repo
|
55
|
+
vBucket can serve as a repo for linux packages. GET requests from `yum` or `apt-get` to the API are easy for vBucket.
|
16
56
|
|
17
|
-
|
57
|
+
You can also publish new packages to your repo by doing a POST or PUT. vBucket can be configured with a `post_upload` script to run repo indexing commands after an upload has completed.
|
18
58
|
|
19
|
-
|
59
|
+
### Gem Server
|
60
|
+
In addition to Linux repos, you can also serve gems from vBucket. Simply use the `post_upload` in the configuration to perform a `gem generate_index` on your vBucket directory.
|
20
61
|
|
21
|
-
|
62
|
+
### Container Repo
|
63
|
+
There is a lot of movement in this area right now and containers are all the rage. As container repos become more standardized, it should be possible to host containers much like RPMs in a yum server. Upload your container and any metadata, and rebuild the index with a `post_upload` script.
|
22
64
|
|
23
|
-
|
65
|
+
### Vagrant Box Repo
|
66
|
+
Hate storing your base Vagrant boxes in Dropbox? I do. vBucket is a great way to keep them organized and accessible.
|
24
67
|
|
25
|
-
## Contributing
|
26
68
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
-
5. Create a new Pull Request
|
69
|
+
## Features Coming Soon
|
70
|
+
* File metadata - add metadata to your files!
|
71
|
+
* File query - list files based on metadata!
|
data/Rakefile
CHANGED
data/bin/vbucket
ADDED
data/config.ru
ADDED
data/config/vbucket.conf
ADDED
data/config/vbucket.keys
ADDED
data/lib/vbucket.rb
CHANGED
@@ -1,5 +1,26 @@
|
|
1
|
-
require
|
1
|
+
require 'vbucket/version'
|
2
|
+
require 'vbucket/service'
|
3
|
+
require 'vbucket/configuration'
|
4
|
+
|
5
|
+
module VBucket
|
6
|
+
class Exception < StandardError; end
|
7
|
+
|
8
|
+
class MissingConfigData < VBucket::Exception
|
9
|
+
def initialize(missing_data_)
|
10
|
+
super("The following data is missing from the config file: #{missing_data_}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class MissingConfigFile < VBucket::Exception
|
15
|
+
def initialize(file_path_)
|
16
|
+
super("The config file is missing from path: #{file_path_}")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class CannotAccessShare < VBucket::Exception
|
21
|
+
def initialize(file_path_)
|
22
|
+
super("Directory does not exist or permissions issue: #{file_path_}")
|
23
|
+
end
|
24
|
+
end
|
2
25
|
|
3
|
-
module Vbucket
|
4
|
-
# Your code goes here...
|
5
26
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require_relative '../../lib/vbucket'
|
3
|
+
|
4
|
+
module VBucket
|
5
|
+
class Configuration
|
6
|
+
attr_reader :share, :config_path
|
7
|
+
|
8
|
+
def initialize(path_ = default_path)
|
9
|
+
@config_path = path_ || default_path
|
10
|
+
raise VBucket::MissingConfigFile, @config_path unless File.exist? @config_path
|
11
|
+
config_data = YAML.load_file(@config_path)
|
12
|
+
@share = chk_data config_data[:share]
|
13
|
+
raise VBucket::CannotAccessShare, @share unless share_exist?(@share)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def default_path
|
19
|
+
File.expand_path(File.join(File.dirname(__FILE__), '../../config/vbucket.conf'))
|
20
|
+
end
|
21
|
+
|
22
|
+
def chk_data(data_)
|
23
|
+
raise VBucket::MissingConfigData, data_ unless data_
|
24
|
+
data_
|
25
|
+
end
|
26
|
+
|
27
|
+
def share_exist?(path_)
|
28
|
+
Dir.exist? path_
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'sinatra/json'
|
3
|
+
require 'sinatra/respond_with' #respond with multiple content types
|
4
|
+
require 'logger'
|
5
|
+
require 'nokogiri'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'find'
|
8
|
+
require_relative 'configuration'
|
9
|
+
|
10
|
+
module VBucket
|
11
|
+
class Service < Sinatra::Base
|
12
|
+
|
13
|
+
helpers Sinatra::JSON
|
14
|
+
register Sinatra::RespondWith
|
15
|
+
configure :production, :development do
|
16
|
+
enable :logging
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(
|
20
|
+
config = VBucket::Configuration.new,
|
21
|
+
logger = Logger.new(default_log_location)
|
22
|
+
)
|
23
|
+
super()
|
24
|
+
@logger = logger
|
25
|
+
@config = config
|
26
|
+
@logger.debug "Using Config file: #{@config.config_path}"
|
27
|
+
@logger.debug "vBucket File Root: #{@config.share}"
|
28
|
+
|
29
|
+
@share = @config.share
|
30
|
+
end
|
31
|
+
|
32
|
+
before do
|
33
|
+
log_transaction
|
34
|
+
end
|
35
|
+
|
36
|
+
# TODO: RSS support?
|
37
|
+
get '/*', provides: [:json, :html, :xml] do
|
38
|
+
halt 404 unless path_exist?
|
39
|
+
if FileTest.file?(absolute_path)
|
40
|
+
send_file absolute_path, :filename => File.basename(absolute_path), :type => 'Application/octet-stream'
|
41
|
+
else
|
42
|
+
respond_with_file_list
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# get '/:file_path' do |file_path|
|
47
|
+
# halt 404 unless File.exist?(absolute_path(file_path))
|
48
|
+
#
|
49
|
+
# send_file absolute_path(file_path), :filename => File.basename(absolute_path(file_path)), :type => 'Application/octet-stream'
|
50
|
+
# end
|
51
|
+
|
52
|
+
head '/*' do
|
53
|
+
File.exist?(absolute_path) ? (status 200) : (halt 404)
|
54
|
+
response.headers['Content-Length'] = File.size(file)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Upload a file
|
58
|
+
# TODO: We want to POST to specific folders for applications like gem server. (post to /gems/)
|
59
|
+
post '/' do
|
60
|
+
halt 400 unless params[:file]
|
61
|
+
(File.exist? File.join(@share, params[:file][:filename])) ? (status 200) : (status 201)
|
62
|
+
File.open(File.join(@share, params[:file][:filename]), 'wb') { |f| f.write(params[:file][:tempfile].read) }
|
63
|
+
body nil
|
64
|
+
|
65
|
+
# TODO: post_upload_directive
|
66
|
+
# TODO: Do we need to clean up the file if the transfer is unsuccessful?
|
67
|
+
# TODO: rescue IO errors
|
68
|
+
end
|
69
|
+
|
70
|
+
# Create a folder path. When specifying nested paths, any missing folders in that path will be created.
|
71
|
+
put('/folder/*') do
|
72
|
+
(Dir.exist? File.join(@share, params[:splat])) ? (halt 409) : (status 201)
|
73
|
+
FileUtils.mkdir_p File.join(@share, params[:splat])
|
74
|
+
end
|
75
|
+
|
76
|
+
# Upload a file to specific file path
|
77
|
+
put('/*') do
|
78
|
+
(File.exist? absolute_path) ? (status 200) : (status 201)
|
79
|
+
File.open(absolute_path, 'wb') { |file| file.write(request.body.read) }
|
80
|
+
body nil
|
81
|
+
|
82
|
+
# TODO: post_upload_directive
|
83
|
+
# TODO: PUT Folders or Files into folders (for Gem server use case)
|
84
|
+
# TODO: Streaming upload?
|
85
|
+
end
|
86
|
+
|
87
|
+
delete '/*' do
|
88
|
+
halt 404 unless path_exist?
|
89
|
+
File.delete(absolute_path)
|
90
|
+
status 200
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def absolute_path
|
96
|
+
@absolute_path ||= File.join(@share, params[:splat])
|
97
|
+
end
|
98
|
+
|
99
|
+
def path_exist?
|
100
|
+
File.exist?(absolute_path) || File.directory?(absolute_path)
|
101
|
+
end
|
102
|
+
|
103
|
+
def default_log_location
|
104
|
+
File.expand_path(File.join(File.dirname(__FILE__), '../../log/vbucket.log'))
|
105
|
+
end
|
106
|
+
|
107
|
+
def xml_file_list(files)
|
108
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
109
|
+
xml.files {
|
110
|
+
files.each do |filename|
|
111
|
+
xml.file filename
|
112
|
+
end
|
113
|
+
}
|
114
|
+
end
|
115
|
+
builder.to_xml
|
116
|
+
end
|
117
|
+
|
118
|
+
def log_transaction
|
119
|
+
@logger.debug "#{request.ip} - #{request.request_method} #{request.path} #{request.accept}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def respond_with_file_list
|
123
|
+
files = file_list absolute_path
|
124
|
+
respond_to do |accept|
|
125
|
+
accept.xml { xml_file_list files }
|
126
|
+
accept.json { json files }
|
127
|
+
accept.html { files }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def file_list(path)
|
132
|
+
all_entities = Dir.glob(File.join(path, '*'))
|
133
|
+
listing = {}
|
134
|
+
listing[:files] = all_entities.select { |entity| FileTest.file? entity }
|
135
|
+
listing[:folders] = all_entities.select { |entity| FileTest.directory? entity }
|
136
|
+
|
137
|
+
listing.each do |entity_type,list|
|
138
|
+
list.each_index do |index|
|
139
|
+
listing[entity_type][index].sub!(@share, request.url)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
listing
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
data/lib/vbucket/version.rb
CHANGED
data/log/.gitignore
ADDED
data/spec/assets/cat.jpg
ADDED
Binary file
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
describe 'VBucket::Configuration' do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
allow(YAML).to receive(:load_file) {
|
7
|
+
{
|
8
|
+
auth_file: 'vbucket.keys',
|
9
|
+
share: '/example/vbucket/'
|
10
|
+
}
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#new' do
|
15
|
+
#TODO: refactor using rspec subject and contexts
|
16
|
+
|
17
|
+
it 'loads a configuration file' do
|
18
|
+
allow(Dir).to receive(:exist?).with('/example/vbucket/') { true }
|
19
|
+
test_config = VBucket::Configuration.new
|
20
|
+
expect(test_config.share).to eq('/example/vbucket/')
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'raises exception if dir does not exist' do
|
24
|
+
allow(Dir).to receive(:exist?).with('/example/vbucket/') { false }
|
25
|
+
expect { VBucket::Configuration.new }.to raise_error(VBucket::CannotAccessShare)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'raises an error if required data is missing' do
|
29
|
+
allow(YAML).to receive(:load_file) { {auth_file: 'vbucket.keys'} } # Missing share
|
30
|
+
|
31
|
+
expect { VBucket::Configuration.new }.to raise_error(VBucket::MissingConfigData)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'raises an error if config file is missing' do
|
35
|
+
allow(File).to receive(:exist?) { false }
|
36
|
+
|
37
|
+
expect { VBucket::Configuration.new('') }.to raise_error(VBucket::MissingConfigFile)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
describe 'VBucket::Service' do
|
4
|
+
|
5
|
+
let(:header) { {'HTTP_AUTHORIZATION' => 'Token 527337312fc400145d75b6d0e3640253', Accept: 'application/json'} }
|
6
|
+
let(:file_list) {%w(/example/vbucket/1.txt /example/vbucket/2.txt /example/vbucket/bar /example/vbucket/baz.jpg /example/vbucket/foo /example/vbucket/qux.pdf)}
|
7
|
+
def app
|
8
|
+
VBucket::Service
|
9
|
+
end
|
10
|
+
|
11
|
+
before(:example) do
|
12
|
+
allow(Dir).to receive(:glob) { file_list }
|
13
|
+
allow(YAML).to receive(:load_file) { {share: '/example/vbucket/'} }
|
14
|
+
allow(File).to receive(:exist?).with('/Users/jrobinson/vbucket/spec/unit/vbucket/../../assets/cat.jpg') { true }
|
15
|
+
allow(File).to receive(:exist?).with('/Users/jrobinson/vbucket/config/vbucket.conf') { true }
|
16
|
+
allow(File).to receive(:exist?).with('/Users/jrobinson/vbucket/lib/vbucket/public') { false }
|
17
|
+
allow(File).to receive(:exist?).with('/example/vbucket/thisShouldBeA404') { false }
|
18
|
+
allow(File).to receive(:exist?) { true }
|
19
|
+
allow(FileTest).to receive(:file?).with('/example/vbucket/') { false }
|
20
|
+
allow(FileTest).to receive(:file?).with('/example/vbucket/cat.jpg') { true }
|
21
|
+
file_list.each { |file| allow(FileTest).to receive(:file?).with(file) { true } }
|
22
|
+
allow_any_instance_of(VBucket::Service).to receive(:send_file) { 'This is a test' }
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'run' do
|
26
|
+
context 'missing config data' do
|
27
|
+
it 'shuts down'
|
28
|
+
it 'gives a meaningful error message'
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
#TODO: refactor with contexts and subject
|
34
|
+
describe 'GET' do
|
35
|
+
|
36
|
+
it 'GETs /' do
|
37
|
+
allow(Dir).to receive(:exist?) { true }
|
38
|
+
get '/', nil, header
|
39
|
+
expect(last_response).to be_ok
|
40
|
+
expect(last_response.body).to eq('{"files":["http://example.org/1.txt","http://example.org/2.txt","http://example.org/bar","http://example.org/baz.jpg","http://example.org/foo","http://example.org/qux.pdf"],"folders":[]}')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'GETs /:filename' do
|
44
|
+
allow(File).to receive(:exist?) { true }
|
45
|
+
allow(Dir).to receive(:exist?) { true }
|
46
|
+
|
47
|
+
get '/1.txt', nil, header
|
48
|
+
expect(last_response).to be_ok
|
49
|
+
expect(Digest::MD5.hexdigest(last_response.body)).to eq('ce114e4501d2f4e2dcea3e17b546f339')
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'responds with 404 when file does not exist' do
|
53
|
+
allow(File).to receive(:exist?) { false }
|
54
|
+
allow(Dir).to receive(:exist?) { true }
|
55
|
+
|
56
|
+
get '/thisShouldBeA404', nil, header
|
57
|
+
expect(last_response.status).to eq(404)
|
58
|
+
end
|
59
|
+
|
60
|
+
# This is covered by Rack::Protection::PathTraversal
|
61
|
+
# it 'does not allow modifying files outside of share' do
|
62
|
+
# get '/../busted.txt', nil, header
|
63
|
+
# expect(last_response.status).to eq(404)
|
64
|
+
# end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'HEAD' do
|
68
|
+
|
69
|
+
# TODO: Mock Rack::File or move this to integration test?
|
70
|
+
it 'HEADs /:filename' do
|
71
|
+
allow(File).to receive(:exist?) { true }
|
72
|
+
allow(Dir).to receive(:exist?) { true }
|
73
|
+
head '/1.txt', nil, header
|
74
|
+
expect(last_response).to be_ok
|
75
|
+
#expect(last_response.header['Content-Length']).to eq('14')
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'responds with 404 when file does not exist' do
|
79
|
+
allow(File).to receive(:exist?) { false }
|
80
|
+
allow(Dir).to receive(:exist?) { true }
|
81
|
+
head '/thisShouldBeA404', nil, header
|
82
|
+
expect(last_response.status).to eq(404)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'POST' do
|
87
|
+
|
88
|
+
# # # This test is not working due to params not being passed. post using curl works fine.
|
89
|
+
# it 'POSTs file to /' do
|
90
|
+
# test_file = Rack::Test::UploadedFile.new(File.join(File.dirname(__FILE__), '../../assets/cat.jpg'), 'image/jpeg', true)
|
91
|
+
# allow_any_instance_of(VBucket::Authentication).to receive(:has_permission?) { true }
|
92
|
+
# allow(File).to receive(:exist?).with(/\/example\/vbucket\/var\/folders\/.*/) { false }
|
93
|
+
# allow(File).to receive(:open)
|
94
|
+
#
|
95
|
+
# post '/', {'file' => test_file}, header
|
96
|
+
# expect(last_response.status).to eq(201)
|
97
|
+
# end
|
98
|
+
|
99
|
+
it 'responds with 400 when missing data'
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
describe 'PUT' do
|
104
|
+
|
105
|
+
it 'PUTs /:filename' do
|
106
|
+
test_file = Rack::Test::UploadedFile.new(File.join(File.dirname(__FILE__), '../../assets/cat.jpg'), 'image/jpeg', true)
|
107
|
+
allow(File).to receive(:exist?).with('/example/vbucket/cat.jpg') { false }
|
108
|
+
allow(Dir).to receive(:exist?) { true }
|
109
|
+
allow(File).to receive(:open) { 4607 }
|
110
|
+
|
111
|
+
put '/cat.jpg', {'file' => test_file}, header
|
112
|
+
expect(last_response.status).to eq(201)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'DELETE' do
|
117
|
+
|
118
|
+
it 'DELETEs /:filename' do
|
119
|
+
allow(File).to receive(:exist?) { true }
|
120
|
+
allow(File).to receive(:delete) { 1 }
|
121
|
+
|
122
|
+
delete '/cat.jpg', nil, header
|
123
|
+
expect(last_response.status).to eq(200)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'responds with 404 when file does not exist' do
|
127
|
+
allow(File).to receive(:exist?) { false }
|
128
|
+
|
129
|
+
delete '/foo', nil, header
|
130
|
+
expect(last_response.status).to eq(404)
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
data/vbucket.gemspec
CHANGED
@@ -4,19 +4,26 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'vbucket/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'vbucket'
|
8
8
|
spec.version = Vbucket::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.
|
12
|
-
spec.
|
13
|
-
spec.
|
9
|
+
spec.authors = ['jakerobinson']
|
10
|
+
spec.email = ['jaker@vbucket.io']
|
11
|
+
spec.description = %q{A RESTful interface for block level storage}
|
12
|
+
spec.summary = %q{vBucket is a simple Ruby app to provide a REST API for your Linux block level storage}
|
13
|
+
spec.homepage = 'http://vbucket.io'
|
14
|
+
spec.license = 'MIT'
|
14
15
|
|
15
|
-
spec.files = `git ls-files
|
16
|
+
spec.files = `git ls-files`.split($/)
|
16
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
-
spec.require_paths = [
|
19
|
+
spec.require_paths = ['lib']
|
19
20
|
|
20
|
-
spec.
|
21
|
-
spec.
|
21
|
+
spec.add_runtime_dependency 'sinatra'
|
22
|
+
spec.add_runtime_dependency 'sinatra-contrib'
|
23
|
+
spec.add_runtime_dependency 'rack-ssl'
|
24
|
+
spec.add_runtime_dependency 'nokogiri'
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
27
|
+
spec.add_development_dependency 'rake'
|
28
|
+
spec.add_development_dependency 'rspec'
|
22
29
|
end
|
metadata
CHANGED
@@ -1,58 +1,142 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vbucket
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- jakerobinson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sinatra
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sinatra-contrib
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rack-ssl
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: nokogiri
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
13
69
|
- !ruby/object:Gem::Dependency
|
14
70
|
name: bundler
|
15
71
|
requirement: !ruby/object:Gem::Requirement
|
16
72
|
requirements:
|
17
73
|
- - ~>
|
18
74
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
75
|
+
version: '1.3'
|
20
76
|
type: :development
|
21
77
|
prerelease: false
|
22
78
|
version_requirements: !ruby/object:Gem::Requirement
|
23
79
|
requirements:
|
24
80
|
- - ~>
|
25
81
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
82
|
+
version: '1.3'
|
27
83
|
- !ruby/object:Gem::Dependency
|
28
84
|
name: rake
|
29
85
|
requirement: !ruby/object:Gem::Requirement
|
30
86
|
requirements:
|
31
|
-
- -
|
87
|
+
- - '>='
|
32
88
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
89
|
+
version: '0'
|
34
90
|
type: :development
|
35
91
|
prerelease: false
|
36
92
|
version_requirements: !ruby/object:Gem::Requirement
|
37
93
|
requirements:
|
38
|
-
- -
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
39
109
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
41
|
-
description:
|
110
|
+
version: '0'
|
111
|
+
description: A RESTful interface for block level storage
|
42
112
|
email:
|
43
113
|
- jaker@vbucket.io
|
44
|
-
executables:
|
114
|
+
executables:
|
115
|
+
- vbucket
|
45
116
|
extensions: []
|
46
117
|
extra_rdoc_files: []
|
47
118
|
files:
|
48
119
|
- .gitignore
|
49
120
|
- Gemfile
|
121
|
+
- Gemfile.lock
|
122
|
+
- LICENSE.txt
|
50
123
|
- README.md
|
51
124
|
- Rakefile
|
125
|
+
- bin/vbucket
|
126
|
+
- config.ru
|
127
|
+
- config/vbucket.conf
|
128
|
+
- config/vbucket.keys
|
52
129
|
- lib/vbucket.rb
|
130
|
+
- lib/vbucket/configuration.rb
|
131
|
+
- lib/vbucket/service.rb
|
53
132
|
- lib/vbucket/version.rb
|
133
|
+
- log/.gitignore
|
134
|
+
- spec/assets/cat.jpg
|
135
|
+
- spec/spec_helper.rb
|
136
|
+
- spec/unit/vbucket/configuration_spec.rb
|
137
|
+
- spec/unit/vbucket/service_spec.rb
|
54
138
|
- vbucket.gemspec
|
55
|
-
homepage:
|
139
|
+
homepage: http://vbucket.io
|
56
140
|
licenses:
|
57
141
|
- MIT
|
58
142
|
metadata: {}
|
@@ -75,6 +159,10 @@ rubyforge_project:
|
|
75
159
|
rubygems_version: 2.2.2
|
76
160
|
signing_key:
|
77
161
|
specification_version: 4
|
78
|
-
summary: vBucket is a REST API for
|
79
|
-
|
80
|
-
|
162
|
+
summary: vBucket is a simple Ruby app to provide a REST API for your Linux block level
|
163
|
+
storage
|
164
|
+
test_files:
|
165
|
+
- spec/assets/cat.jpg
|
166
|
+
- spec/spec_helper.rb
|
167
|
+
- spec/unit/vbucket/configuration_spec.rb
|
168
|
+
- spec/unit/vbucket/service_spec.rb
|