openstack-swift 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +1 -1
- data/Gemfile.lock +43 -0
- data/config/swift.yml +4 -0
- data/lib/openstack-swift.rb +37 -1
- data/lib/openstack-swift/api.rb +147 -0
- data/lib/openstack-swift/client.rb +121 -0
- data/lib/openstack-swift/errors.rb +4 -0
- data/lib/openstack-swift/swift_config.rb +19 -0
- data/lib/openstack-swift/version.rb +2 -1
- data/openstack-swift.gemspec +7 -5
- data/spec/openstack-swift/client_spec.rb +47 -0
- data/spec/openstack-swift/web_api_spec.rb +99 -0
- data/spec/spec_helper.rb +13 -0
- metadata +67 -35
- data/.gitignore +0 -4
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
openstack-swift (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://gems.locaweb.com.br/
|
8
|
+
specs:
|
9
|
+
archive-tar-minitar (0.5.2)
|
10
|
+
columnize (0.3.4)
|
11
|
+
crack (0.1.8)
|
12
|
+
diff-lcs (1.1.2)
|
13
|
+
httparty (0.7.8)
|
14
|
+
crack (= 0.1.8)
|
15
|
+
linecache19 (0.5.12)
|
16
|
+
ruby_core_source (>= 0.1.4)
|
17
|
+
rspec (2.6.0)
|
18
|
+
rspec-core (~> 2.6.0)
|
19
|
+
rspec-expectations (~> 2.6.0)
|
20
|
+
rspec-mocks (~> 2.6.0)
|
21
|
+
rspec-core (2.6.4)
|
22
|
+
rspec-expectations (2.6.0)
|
23
|
+
diff-lcs (~> 1.1.2)
|
24
|
+
rspec-mocks (2.6.0)
|
25
|
+
ruby-debug-base19 (0.11.25)
|
26
|
+
columnize (>= 0.3.1)
|
27
|
+
linecache19 (>= 0.5.11)
|
28
|
+
ruby_core_source (>= 0.1.4)
|
29
|
+
ruby-debug19 (0.11.6)
|
30
|
+
columnize (>= 0.3.1)
|
31
|
+
linecache19 (>= 0.5.11)
|
32
|
+
ruby-debug-base19 (>= 0.11.19)
|
33
|
+
ruby_core_source (0.1.5)
|
34
|
+
archive-tar-minitar (>= 0.5.2)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
httparty
|
41
|
+
openstack-swift!
|
42
|
+
rspec (~> 2.6)
|
43
|
+
ruby-debug19
|
data/config/swift.yml
ADDED
data/lib/openstack-swift.rb
CHANGED
@@ -1,5 +1,41 @@
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
2
|
+
require "httparty"
|
3
|
+
require "net/http"
|
4
|
+
require "net/https"
|
5
|
+
require "fileutils"
|
6
|
+
require "openstack-swift/api"
|
7
|
+
require "openstack-swift/errors"
|
8
|
+
require "openstack-swift/client"
|
9
|
+
require "openstack-swift/swift_config"
|
10
|
+
|
1
11
|
module Openstack
|
2
12
|
module Swift
|
3
|
-
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Net
|
17
|
+
class HTTPGenericRequest
|
18
|
+
private
|
19
|
+
def send_request_with_body_stream(sock, ver, path, f)
|
20
|
+
unless content_length() or chunked?
|
21
|
+
raise ArgumentError,
|
22
|
+
"Content-Length not given and Transfer-Encoding is not `chunked'"
|
23
|
+
end
|
24
|
+
supply_default_content_type
|
25
|
+
write_header sock, ver, path
|
26
|
+
if chunked?
|
27
|
+
while s = f.read(1024)
|
28
|
+
sock.write(sprintf("%x\r\n", s.length) << s << "\r\n")
|
29
|
+
end
|
30
|
+
sock.write "0\r\n\r\n"
|
31
|
+
else
|
32
|
+
bytes_written = 0
|
33
|
+
buffer=1024
|
34
|
+
while (s = f.read(buffer)) && bytes_written < content_length()
|
35
|
+
sock.write s
|
36
|
+
bytes_written += buffer
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
4
40
|
end
|
5
41
|
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
2
|
+
module Openstack
|
3
|
+
module Swift
|
4
|
+
module Api
|
5
|
+
extend self
|
6
|
+
|
7
|
+
# Authentication method to get the url and token to conect to swift
|
8
|
+
# Returns:
|
9
|
+
# x-storage-url
|
10
|
+
# x-storage-token
|
11
|
+
# x-auth-token
|
12
|
+
def auth(proxy, user, password)
|
13
|
+
res = HTTParty.get(proxy, :headers => {"X-Auth-User" => user, "X-Auth-Key" => password})
|
14
|
+
raise AuthenticationError unless res.code == 200
|
15
|
+
|
16
|
+
[res.headers["x-storage-url"],res.headers["x-storage-token"],res.headers["x-auth-token"]]
|
17
|
+
end
|
18
|
+
|
19
|
+
# Get informations about the currect account used to connect to swift
|
20
|
+
# Returns:
|
21
|
+
# x-account-bytes-used
|
22
|
+
# x-account-object-count
|
23
|
+
# x-account-container-count
|
24
|
+
def account(url, token)
|
25
|
+
query = {:format => "json"}
|
26
|
+
HTTParty.head(url, :headers => {"X-Auth-Token"=> token}, :query => query).headers
|
27
|
+
end
|
28
|
+
|
29
|
+
# List containers
|
30
|
+
# Note that swift only returns 1000 items, so to list more than this
|
31
|
+
# you should use the marker option as the name of the last returned item (1000th item)
|
32
|
+
# to return the next sequency (1001 - 2000)
|
33
|
+
# query options: marker, prefix, limit
|
34
|
+
def containers(url, token, query = {})
|
35
|
+
query = query.merge(:format => "json")
|
36
|
+
res = HTTParty.get(url, :headers => {"X-Auth-Token"=> token}, :query => query)
|
37
|
+
res.to_a
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get all objects for a given container
|
41
|
+
# Query options:
|
42
|
+
# marker
|
43
|
+
# prefix
|
44
|
+
# limit
|
45
|
+
# delimiter
|
46
|
+
def objects(url, token, container, query = {})
|
47
|
+
query = query.merge(:format => "json")
|
48
|
+
res = HTTParty.get("#{url}/#{container}", :headers => {"X-Auth-Token"=> token}, :query => query)
|
49
|
+
res.to_a
|
50
|
+
end
|
51
|
+
|
52
|
+
# Delete a container for a given name from swift
|
53
|
+
def delete_container(url, token, container)
|
54
|
+
res = HTTParty.delete("#{url}/#{container}", :headers => {"X-Auth-Token"=> token})
|
55
|
+
raise "Could not delete container '#{container}'" if res.code < 200 or res.code >= 300
|
56
|
+
true
|
57
|
+
end
|
58
|
+
|
59
|
+
# Create a container on swift from a given name
|
60
|
+
def create_container(url, token, container)
|
61
|
+
res = HTTParty.put("#{url}/#{container}", :headers => {"X-Auth-Token"=> token})
|
62
|
+
raise "Could not create container '#{container}'" if res.code < 200 or res.code >= 300
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
# Get the object stat given the object name and the container this object is in
|
67
|
+
def object_stat(url, token, container, object)
|
68
|
+
url = "#{url}/#{container}/#{object}"
|
69
|
+
query = {:format => "json"}
|
70
|
+
HTTParty.head(url, :headers => {"X-Auth-Token"=> token}, :query => query).headers
|
71
|
+
end
|
72
|
+
|
73
|
+
# Creates the manifest file for a splitted upload
|
74
|
+
# Given the container and file path a manifest is created to guide the downloads of this
|
75
|
+
# splitted file
|
76
|
+
def create_manifest(url, token, container, file_path)
|
77
|
+
file_name = file_path.match(/.+\/(.+?)$/)[1]
|
78
|
+
file_size = File.size(file_path)
|
79
|
+
file_mtime = File.mtime(file_path).to_f.round(2)
|
80
|
+
manifest_path = "#{container}_segments/#{file_name}/#{file_mtime}/#{file_size}/"
|
81
|
+
|
82
|
+
res = HTTParty.put("#{url}/#{container}/#{file_name}", :headers => {
|
83
|
+
"X-Auth-Token" => token,
|
84
|
+
"x-object-manifest" => manifest_path,
|
85
|
+
"Content-Type" => "application/octet-stream",
|
86
|
+
"Content-Length" => "0"
|
87
|
+
})
|
88
|
+
|
89
|
+
raise "Could not create manifest for '#{file_path}'" if res.code < 200 or res.code >= 300
|
90
|
+
true
|
91
|
+
end
|
92
|
+
|
93
|
+
# Downloads an object (file) to disk and returns the saved file path
|
94
|
+
def download_object(url, token, container, object, file_name=nil)
|
95
|
+
file_name ||= "/tmp/swift/#{container}/#{object}"
|
96
|
+
|
97
|
+
# creating directory if it doesn't exist
|
98
|
+
FileUtils.mkdir_p(file_name.match(/(.+)\/.+?$/)[1])
|
99
|
+
file = File.open(file_name, "wb")
|
100
|
+
uri = URI.parse("#{url}/#{container}/#{object}")
|
101
|
+
|
102
|
+
req = Net::HTTP::Get.new(uri.path)
|
103
|
+
req.add_field("X-Auth-Token", token)
|
104
|
+
|
105
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
106
|
+
http.use_ssl = true
|
107
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
108
|
+
md5 = Digest::MD5.new
|
109
|
+
|
110
|
+
http.request(req) do |res|
|
111
|
+
res.read_body do |chunk|
|
112
|
+
file.write chunk
|
113
|
+
md5.update(chunk)
|
114
|
+
end
|
115
|
+
|
116
|
+
raise "MD5 checksum failed for #{container}/#{object}" if res["x-object-manifest"].nil? && res["etag"] != md5.hexdigest
|
117
|
+
end
|
118
|
+
|
119
|
+
file_name
|
120
|
+
ensure
|
121
|
+
file.close rescue nil
|
122
|
+
end
|
123
|
+
|
124
|
+
# Uploads a given object to a given container
|
125
|
+
def upload_object(url, token, container, file_path, options={})
|
126
|
+
options[:object_name] ||= file_path.match(/.+\/(.+?)$/)[1]
|
127
|
+
file = File.open(file_path, "rb")
|
128
|
+
|
129
|
+
file.seek(options[:position]) if options[:position]
|
130
|
+
uri = URI.parse("#{url}/#{container}/#{options[:object_name]}")
|
131
|
+
|
132
|
+
req = Net::HTTP::Put.new(uri.path)
|
133
|
+
req.add_field("X-Auth-Token", token)
|
134
|
+
req.body_stream = file
|
135
|
+
req.content_length = options[:size] || File.size(file_path)
|
136
|
+
req.content_type = "application/octet-stream"
|
137
|
+
|
138
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
139
|
+
http.use_ssl = true
|
140
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
141
|
+
http.request(req)
|
142
|
+
ensure
|
143
|
+
file.close rescue nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
2
|
+
module Openstack
|
3
|
+
module Swift
|
4
|
+
class Client
|
5
|
+
SWIFT_API = Openstack::Swift::Api
|
6
|
+
MAX_SIZE = 4 * 1024 ** 3
|
7
|
+
|
8
|
+
# Initialize method
|
9
|
+
# It uses the authenticate method to store the tokens for future requests
|
10
|
+
def initialize(proxy, user, password)
|
11
|
+
@proxy, @user, @password = proxy, user, password
|
12
|
+
authenticate!
|
13
|
+
end
|
14
|
+
|
15
|
+
# Authentication method
|
16
|
+
# It stores the authentication url and token for future commands
|
17
|
+
# avoiding to request a new token for each request
|
18
|
+
# It should be used to force a new token
|
19
|
+
def authenticate!
|
20
|
+
@url, _, @token = SWIFT_API.auth(@proxy, @user, @password)
|
21
|
+
|
22
|
+
if @url.blank? or @token.blank?
|
23
|
+
raise AuthenticationError
|
24
|
+
else
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns the following informations about the object:
|
30
|
+
# bytes_used: Number of bytes used by this account
|
31
|
+
# object_count: Number of objects that this account have allocated
|
32
|
+
# container_count: Number of container
|
33
|
+
def account_info
|
34
|
+
headers = SWIFT_API.account(@url, @token)
|
35
|
+
{
|
36
|
+
"bytes_used" => headers["x-account-bytes-used"],
|
37
|
+
"object_count" => headers["x-account-object-count"],
|
38
|
+
"container_count" => headers["x-account-container-count"]
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the following informations about the account:
|
43
|
+
# last_modified
|
44
|
+
# md5
|
45
|
+
# content_type
|
46
|
+
# manifest
|
47
|
+
# content_length
|
48
|
+
def object_info(container, object)
|
49
|
+
headers = SWIFT_API.object_stat(@url, @token, container, object)
|
50
|
+
{
|
51
|
+
"last_modified" => headers["last-modified"],
|
52
|
+
"md5" => headers["etag"],
|
53
|
+
"content_type" => headers["content-type"],
|
54
|
+
"manifest" => headers["x-object-manifest"],
|
55
|
+
"content_length" => headers["content-length"]
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
# This method uploads a file from a given to a given container
|
60
|
+
def upload(container, file_path, options={})
|
61
|
+
options[:segments_size] ||= MAX_SIZE
|
62
|
+
|
63
|
+
SWIFT_API.create_container(@url, @token, container) rescue nil
|
64
|
+
|
65
|
+
file_name, file_mtime, file_size = file_info(file_path)
|
66
|
+
|
67
|
+
if file_size > options[:segments_size]
|
68
|
+
SWIFT_API.create_container(@url, @token, "#{container}_segments") rescue nil
|
69
|
+
|
70
|
+
segments_minus_one = file_size / options[:segments_size]
|
71
|
+
last_piece = file_size - segments_minus_one * options[:segments_size]
|
72
|
+
segments_minus_one.times do |segment|
|
73
|
+
upload_path_for(file_path, segment)
|
74
|
+
SWIFT_API.upload_object(
|
75
|
+
@url, @token, "#{container}_segments", file_path,
|
76
|
+
:size => options[:segments_size],
|
77
|
+
:position => options[:segments_size] * segment,
|
78
|
+
:object_name => segment_path
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
SWIFT_API.upload_object(
|
83
|
+
@url, @token, "#{container}_segments", file_path,
|
84
|
+
:size => last_piece,
|
85
|
+
:position => options[:segments_size] * segments_minus_one,
|
86
|
+
:object_name => upload_path_for(file_path, segments_minus_one)
|
87
|
+
)
|
88
|
+
|
89
|
+
SWIFT_API.create_manifest(@url, @token, container, file_path)
|
90
|
+
else
|
91
|
+
SWIFT_API.upload_object(@url, @token, container, file_path)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# This method downloads a object from a given container
|
96
|
+
def download(container, object)
|
97
|
+
SWIFT_API.download_object(@url, @token, container, object)
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
# Returns the standard swift path for a given file path and segment
|
103
|
+
def upload_path_for(file_path, segment)
|
104
|
+
"%s/%s/s/%08d" % (file_info(file_path) << segment)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Get relevant informations about a file
|
108
|
+
# Returns an array with:
|
109
|
+
# file_name
|
110
|
+
# file_mtime
|
111
|
+
# file_size
|
112
|
+
def file_info(file_path)
|
113
|
+
[
|
114
|
+
file_path.match(/.+\/(.+?)$/)[1],
|
115
|
+
File.mtime(file_path).to_f.round(2),
|
116
|
+
File.size(file_path)
|
117
|
+
]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
2
|
+
module Openstack
|
3
|
+
module SwiftConfig
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def [](name)
|
7
|
+
configs[name]
|
8
|
+
end
|
9
|
+
|
10
|
+
module_function
|
11
|
+
def configs
|
12
|
+
@config_file ||= load_file
|
13
|
+
end
|
14
|
+
|
15
|
+
def load_file
|
16
|
+
YAML.load_file(File.expand_path(File.dirname(__FILE__)) + "/../../config/swift.yml")[:swift]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/openstack-swift.gemspec
CHANGED
@@ -8,14 +8,16 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["morellon", "pothix"]
|
10
10
|
s.email = ["morellon@gmail.com", "pothix@pothix.com"]
|
11
|
-
s.homepage = ""
|
12
|
-
s.summary = %q{Openstack's swift client}
|
11
|
+
s.homepage = "http://github.com/morellon/openstack-swift"
|
13
12
|
s.description = %q{Openstack's swift client}
|
13
|
+
s.summary = s.description
|
14
14
|
|
15
15
|
s.rubyforge_project = "openstack-swift"
|
16
16
|
|
17
|
-
s.files =
|
18
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.files = Dir["./**/*"].reject {|file| file =~ /\.git|pkg/}
|
20
18
|
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_development_dependency "rspec", "~> 2.6"
|
21
|
+
s.add_development_dependency "httparty"
|
22
|
+
s.add_development_dependency "ruby-debug19"
|
21
23
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe "Openstack::Swift::Client" do
|
5
|
+
let!(:swift_dummy_file) do
|
6
|
+
file_path = "/tmp/swift-dummy"
|
7
|
+
File.open(file_path, "w") {|f| f.puts("testfile "*1000)}
|
8
|
+
file_path
|
9
|
+
end
|
10
|
+
|
11
|
+
context "when authenticating" do
|
12
|
+
it "should return authentication error if one of the parameters is incorrect" do
|
13
|
+
expect {
|
14
|
+
Openstack::Swift::Client.new("http://incorrect.com/swift", Openstack::SwiftConfig[:user], Openstack::SwiftConfig[:pass])
|
15
|
+
}.to raise_error(Openstack::Swift::AuthenticationError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "shoud not need to authenticate again" do
|
19
|
+
client = Openstack::Swift::Client.new(Openstack::SwiftConfig[:url], Openstack::SwiftConfig[:user], Openstack::SwiftConfig[:pass])
|
20
|
+
Openstack::Swift::Api.should_not_receive(:auth)
|
21
|
+
client.account_info
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when authenticated" do
|
26
|
+
subject { Openstack::Swift::Client.new(Openstack::SwiftConfig[:url], Openstack::SwiftConfig[:user], Openstack::SwiftConfig[:pass]) }
|
27
|
+
|
28
|
+
it "should upload a splitted file and create its manifest" do
|
29
|
+
pending "WTF...Not working for a unknown reason"
|
30
|
+
subject.upload("pothix", swift_dummy_file, {:segments_size => 1024*2})
|
31
|
+
subject.object_info("pothix", "swifty-dummy")["manifest"].should_not be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should download an splitted file" do
|
35
|
+
content_length = subject.object_info("morellon", "splitted_file")["content_length"].to_i
|
36
|
+
file_path = subject.download("morellon", "splitted_file")
|
37
|
+
File.size(file_path).should == content_length
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should return account's details" do
|
41
|
+
account_info = subject.account_info
|
42
|
+
account_info.should have_key("bytes_used")
|
43
|
+
account_info.should have_key("object_count")
|
44
|
+
account_info.should have_key("container_count")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe Openstack::Swift::Api do
|
5
|
+
context "when authenticating" do
|
6
|
+
it "should authenticate on swift" do
|
7
|
+
expect {
|
8
|
+
subject.auth(Openstack::SwiftConfig[:url], Openstack::SwiftConfig[:user], Openstack::SwiftConfig[:pass])
|
9
|
+
}.to_not raise_error Openstack::Swift::AuthenticationError
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should raise error for a invalid url" do
|
13
|
+
expect {
|
14
|
+
subject.auth("http://pothix.com/swift", Openstack::SwiftConfig[:user], Openstack::SwiftConfig[:pass])
|
15
|
+
}.to raise_error Openstack::Swift::AuthenticationError
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should raise error for a invalid pass" do
|
19
|
+
expect {
|
20
|
+
subject.auth(Openstack::SwiftConfig[:url], Openstack::SwiftConfig[:user], "invalidpassword")
|
21
|
+
}.to raise_error Openstack::Swift::AuthenticationError
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should raise error for a invalid user" do
|
25
|
+
expect {
|
26
|
+
subject.auth(Openstack::SwiftConfig[:url], "system:weirduser", Openstack::SwiftConfig[:pass])
|
27
|
+
}.to raise_error Openstack::Swift::AuthenticationError
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should return storage-url, storage-token and auth-token" do
|
31
|
+
subject.auth(Openstack::SwiftConfig[:url], Openstack::SwiftConfig[:user], Openstack::SwiftConfig[:pass]).should have(3).items
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when authenticated" do
|
36
|
+
let!(:swift_dummy_file){ File.open("/tmp/swift-dummy", "w") {|f| f.puts("test file"*1000)} }
|
37
|
+
|
38
|
+
before do
|
39
|
+
@url, _, @token = subject.auth(
|
40
|
+
Openstack::SwiftConfig[:url],
|
41
|
+
Openstack::SwiftConfig[:user],
|
42
|
+
Openstack::SwiftConfig[:pass]
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should return account's headers" do
|
47
|
+
account = subject.account(@url, @token)
|
48
|
+
account.should have_key("x-account-bytes-used")
|
49
|
+
account.should have_key("x-account-object-count")
|
50
|
+
account.should have_key("x-account-container-count")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should return a list of containers" do
|
54
|
+
subject.containers(@url, @token).should be_a(Array)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should return a list of objects" do
|
58
|
+
subject.objects(@url, @token, "morellon", :delimiter => "/").should be_a(Array)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should download an object" do
|
62
|
+
subject.download_object(@url, @token, "morellon", "Gemfile").should == "/tmp/swift/morellon/Gemfile"
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should upload an object" do
|
66
|
+
subject.upload_object(@url, @token, "morellon", "/tmp/swift-dummy").code.should == "201"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should create a new container" do
|
70
|
+
subject.create_container(@url, @token, "pothix_container").should be_true
|
71
|
+
end
|
72
|
+
|
73
|
+
context "when excluding a container" do
|
74
|
+
before { @container = "pothix_container" }
|
75
|
+
|
76
|
+
it "should delete a existent container" do
|
77
|
+
subject.create_container(@url, @token, @container).should be_true
|
78
|
+
subject.delete_container(@url, @token, @container).should be_true
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should raise an error when the container doesn't exist" do
|
82
|
+
expect {
|
83
|
+
subject.delete_container(@url, @token, @container).should be_true
|
84
|
+
subject.delete_container(@url, @token, @container).should be_true
|
85
|
+
}.to raise_error("Could not delete container '#{@container}'")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should get the file stat" do
|
90
|
+
subject.upload_object(@url, @token, "morellon", "/tmp/swift-dummy")
|
91
|
+
headers = subject.object_stat(@url, @token, "morellon", "swift-dummy")
|
92
|
+
|
93
|
+
headers["last-modified"].should_not be_blank
|
94
|
+
headers["etag"].should_not be_blank
|
95
|
+
headers["content-type"].should_not be_blank
|
96
|
+
headers["date"].should_not be_blank
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
2
|
+
require "rubygems"
|
3
|
+
require "bundler/setup"
|
4
|
+
|
5
|
+
require "openstack-swift"
|
6
|
+
|
7
|
+
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|file| require file}
|
8
|
+
|
9
|
+
# Using a diferent YAML engine on test environment
|
10
|
+
YAML::ENGINE::yamler = "syck"
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
end
|
metadata
CHANGED
@@ -1,64 +1,96 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: openstack-swift
|
3
|
-
version: !ruby/object:Gem::Version
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
4
5
|
prerelease:
|
5
|
-
version: 0.0.1
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- morellon
|
9
9
|
- pothix
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
|
14
|
-
date: 2011-08-10 00:00:00 -03:00
|
13
|
+
date: 2011-08-22 00:00:00.000000000 -03:00
|
15
14
|
default_executable:
|
16
|
-
dependencies:
|
17
|
-
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rspec
|
18
|
+
requirement: &10022160 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '2.6'
|
24
|
+
type: :development
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: *10022160
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: httparty
|
29
|
+
requirement: &10021760 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ! '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: *10021760
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: ruby-debug19
|
40
|
+
requirement: &10021280 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
type: :development
|
47
|
+
prerelease: false
|
48
|
+
version_requirements: *10021280
|
18
49
|
description: Openstack's swift client
|
19
|
-
email:
|
50
|
+
email:
|
20
51
|
- morellon@gmail.com
|
21
52
|
- pothix@pothix.com
|
22
53
|
executables: []
|
23
|
-
|
24
54
|
extensions: []
|
25
|
-
|
26
55
|
extra_rdoc_files: []
|
27
|
-
|
28
|
-
|
29
|
-
- .
|
30
|
-
-
|
31
|
-
-
|
32
|
-
- lib/openstack-swift.rb
|
33
|
-
- lib/openstack-swift/
|
34
|
-
- openstack-swift.
|
56
|
+
files:
|
57
|
+
- ./Gemfile.lock
|
58
|
+
- ./config/swift.yml
|
59
|
+
- ./Rakefile
|
60
|
+
- ./lib/openstack-swift.rb
|
61
|
+
- ./lib/openstack-swift/swift_config.rb
|
62
|
+
- ./lib/openstack-swift/errors.rb
|
63
|
+
- ./lib/openstack-swift/api.rb
|
64
|
+
- ./lib/openstack-swift/client.rb
|
65
|
+
- ./lib/openstack-swift/version.rb
|
66
|
+
- ./Gemfile
|
67
|
+
- ./openstack-swift.gemspec
|
68
|
+
- ./spec/openstack-swift/client_spec.rb
|
69
|
+
- ./spec/openstack-swift/web_api_spec.rb
|
70
|
+
- ./spec/spec_helper.rb
|
35
71
|
has_rdoc: true
|
36
|
-
homepage:
|
72
|
+
homepage: http://github.com/morellon/openstack-swift
|
37
73
|
licenses: []
|
38
|
-
|
39
74
|
post_install_message:
|
40
75
|
rdoc_options: []
|
41
|
-
|
42
|
-
require_paths:
|
76
|
+
require_paths:
|
43
77
|
- lib
|
44
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
79
|
none: false
|
46
|
-
requirements:
|
47
|
-
- -
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version:
|
50
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
85
|
none: false
|
52
|
-
requirements:
|
53
|
-
- -
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version:
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
56
90
|
requirements: []
|
57
|
-
|
58
91
|
rubyforge_project: openstack-swift
|
59
92
|
rubygems_version: 1.6.2
|
60
93
|
signing_key:
|
61
94
|
specification_version: 3
|
62
95
|
summary: Openstack's swift client
|
63
96
|
test_files: []
|
64
|
-
|
data/.gitignore
DELETED