openstack-swift 0.1.1 → 0.2.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.
Files changed (54) hide show
  1. data/Gemfile.lock +2 -2
  2. data/README.rdoc +47 -0
  3. data/doc/Gemfile.html +100 -0
  4. data/doc/Object.html +177 -0
  5. data/doc/Openstack.html +162 -0
  6. data/doc/Openstack/Swift.html +182 -0
  7. data/doc/Openstack/Swift/AuthenticationError.html +151 -0
  8. data/doc/Openstack/Swift/Client.html +316 -0
  9. data/doc/Openstack/Swift/UnauthorizedError.html +147 -0
  10. data/doc/Openstack/Swift/WebApi.html +417 -0
  11. data/doc/Rakefile.html +98 -0
  12. data/doc/created.rid +11 -0
  13. data/doc/images/brick.png +0 -0
  14. data/doc/images/brick_link.png +0 -0
  15. data/doc/images/bug.png +0 -0
  16. data/doc/images/bullet_black.png +0 -0
  17. data/doc/images/bullet_toggle_minus.png +0 -0
  18. data/doc/images/bullet_toggle_plus.png +0 -0
  19. data/doc/images/date.png +0 -0
  20. data/doc/images/find.png +0 -0
  21. data/doc/images/loadingAnimation.gif +0 -0
  22. data/doc/images/macFFBgHack.png +0 -0
  23. data/doc/images/package.png +0 -0
  24. data/doc/images/page_green.png +0 -0
  25. data/doc/images/page_white_text.png +0 -0
  26. data/doc/images/page_white_width.png +0 -0
  27. data/doc/images/plugin.png +0 -0
  28. data/doc/images/ruby.png +0 -0
  29. data/doc/images/tag_green.png +0 -0
  30. data/doc/images/wrench.png +0 -0
  31. data/doc/images/wrench_orange.png +0 -0
  32. data/doc/images/zoom.png +0 -0
  33. data/doc/index.html +88 -0
  34. data/doc/js/darkfish.js +116 -0
  35. data/doc/js/jquery.js +32 -0
  36. data/doc/js/quicksearch.js +114 -0
  37. data/doc/js/thickbox-compressed.js +10 -0
  38. data/doc/lib/openstack-swift/client_rb.html +52 -0
  39. data/doc/lib/openstack-swift/errors_rb.html +52 -0
  40. data/doc/lib/openstack-swift/version_rb.html +52 -0
  41. data/doc/lib/openstack-swift/web_api_rb.html +52 -0
  42. data/doc/lib/openstack-swift_rb.html +66 -0
  43. data/doc/rdoc.css +763 -0
  44. data/doc/spec/openstack-swift/client_spec_rb.html +54 -0
  45. data/doc/spec/openstack-swift/web_api_spec_rb.html +54 -0
  46. data/doc/spec/spec_helper_rb.html +58 -0
  47. data/lib/openstack-swift/api.rb +77 -1
  48. data/lib/openstack-swift/client.rb +12 -57
  49. data/lib/openstack-swift/errors.rb +1 -0
  50. data/lib/openstack-swift/version.rb +1 -1
  51. data/spec/openstack-swift/{web_api_spec.rb → api_spec.rb} +23 -2
  52. data/spec/openstack-swift/client_spec.rb +19 -4
  53. metadata +60 -16
  54. data/openstack-swift-0.1.0.gem +0 -0
@@ -0,0 +1,54 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
+ <head>
7
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
8
+
9
+ <title>File: client_spec.rb [RDoc Documentation]</title>
10
+
11
+ <link type="text/css" media="screen" href="../../rdoc.css" rel="stylesheet" />
12
+
13
+ <script src="../../js/jquery.js" type="text/javascript"
14
+ charset="utf-8"></script>
15
+ <script src="../../js/thickbox-compressed.js" type="text/javascript"
16
+ charset="utf-8"></script>
17
+ <script src="../../js/quicksearch.js" type="text/javascript"
18
+ charset="utf-8"></script>
19
+ <script src="../../js/darkfish.js" type="text/javascript"
20
+ charset="utf-8"></script>
21
+ </head>
22
+
23
+ <body class="file file-popup">
24
+ <div id="metadata">
25
+ <dl>
26
+ <dt class="modified-date">Last Modified</dt>
27
+ <dd class="modified-date">2011-08-15 16:04:27 -0300</dd>
28
+
29
+
30
+ <dt class="requires">Requires</dt>
31
+ <dd class="requires">
32
+ <ul>
33
+
34
+ <li>openstack-swift</li>
35
+
36
+ </ul>
37
+ </dd>
38
+
39
+
40
+
41
+ </dl>
42
+ </div>
43
+
44
+ <div id="documentation">
45
+
46
+ <div class="description">
47
+ <h2>Description</h2>
48
+
49
+ </div>
50
+
51
+ </div>
52
+ </body>
53
+ </html>
54
+
@@ -0,0 +1,54 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
+ <head>
7
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
8
+
9
+ <title>File: web_api_spec.rb [RDoc Documentation]</title>
10
+
11
+ <link type="text/css" media="screen" href="../../rdoc.css" rel="stylesheet" />
12
+
13
+ <script src="../../js/jquery.js" type="text/javascript"
14
+ charset="utf-8"></script>
15
+ <script src="../../js/thickbox-compressed.js" type="text/javascript"
16
+ charset="utf-8"></script>
17
+ <script src="../../js/quicksearch.js" type="text/javascript"
18
+ charset="utf-8"></script>
19
+ <script src="../../js/darkfish.js" type="text/javascript"
20
+ charset="utf-8"></script>
21
+ </head>
22
+
23
+ <body class="file file-popup">
24
+ <div id="metadata">
25
+ <dl>
26
+ <dt class="modified-date">Last Modified</dt>
27
+ <dd class="modified-date">2011-08-15 15:34:19 -0300</dd>
28
+
29
+
30
+ <dt class="requires">Requires</dt>
31
+ <dd class="requires">
32
+ <ul>
33
+
34
+ <li>openstack-swift</li>
35
+
36
+ </ul>
37
+ </dd>
38
+
39
+
40
+
41
+ </dl>
42
+ </div>
43
+
44
+ <div id="documentation">
45
+
46
+ <div class="description">
47
+ <h2>Description</h2>
48
+
49
+ </div>
50
+
51
+ </div>
52
+ </body>
53
+ </html>
54
+
@@ -0,0 +1,58 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
+ <head>
7
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
8
+
9
+ <title>File: spec_helper.rb [RDoc Documentation]</title>
10
+
11
+ <link type="text/css" media="screen" href="../rdoc.css" rel="stylesheet" />
12
+
13
+ <script src="../js/jquery.js" type="text/javascript"
14
+ charset="utf-8"></script>
15
+ <script src="../js/thickbox-compressed.js" type="text/javascript"
16
+ charset="utf-8"></script>
17
+ <script src="../js/quicksearch.js" type="text/javascript"
18
+ charset="utf-8"></script>
19
+ <script src="../js/darkfish.js" type="text/javascript"
20
+ charset="utf-8"></script>
21
+ </head>
22
+
23
+ <body class="file file-popup">
24
+ <div id="metadata">
25
+ <dl>
26
+ <dt class="modified-date">Last Modified</dt>
27
+ <dd class="modified-date">2011-08-15 15:33:54 -0300</dd>
28
+
29
+
30
+ <dt class="requires">Requires</dt>
31
+ <dd class="requires">
32
+ <ul>
33
+
34
+ <li>rubygems</li>
35
+
36
+ <li>bundler/setup</li>
37
+
38
+ <li>openstack-swift</li>
39
+
40
+ </ul>
41
+ </dd>
42
+
43
+
44
+
45
+ </dl>
46
+ </div>
47
+
48
+ <div id="documentation">
49
+
50
+ <div class="description">
51
+ <h2>Description</h2>
52
+
53
+ </div>
54
+
55
+ </div>
56
+ </body>
57
+ </html>
58
+
@@ -3,6 +3,7 @@ module Openstack
3
3
  module Swift
4
4
  module Api
5
5
  extend self
6
+ MAX_SIZE = 4 * 1024 ** 3
6
7
 
7
8
  # Authentication method to get the url and token to conect to swift
8
9
  # Returns:
@@ -121,8 +122,64 @@ module Openstack
121
122
  file.close rescue nil
122
123
  end
123
124
 
124
- # Uploads a given object to a given container
125
+ # Delete a object for a given container and object name
126
+ def delete_object(url, token, container, object)
127
+ res = HTTParty.delete("#{url}/#{container}/#{object}", :headers => {"X-Auth-Token"=> token})
128
+ raise "Could not delete object '#{object}'" if res.code < 200 or res.code >= 300
129
+ true
130
+ end
131
+
132
+ # Delete all files listed on a manifest following the swift standard order
133
+ # What it does is try to delete all files in sequence from a given manifest until there are no files to delete
134
+ # returning a 404 error code. We don't know how many files are stored for a manifest file.
135
+ def delete_objects_from_manifest(url, token, container, manifest)
136
+ manifest_info = object_stat(url, token, container, manifest)
137
+ files_path = "#{manifest_info["x-object-manifest"]}%08d"
138
+
139
+ manifest_info["content-length"].to_i.times do |index|
140
+ res = HTTParty.delete("#{url}/#{files_path % index}", :headers => {"X-Auth-Token"=> token})
141
+ raise ObjectNotFoundError if res.code == 404
142
+ end
143
+ rescue ObjectNotFoundError
144
+ true
145
+ end
146
+
125
147
  def upload_object(url, token, container, file_path, options={})
148
+ options[:segments_size] ||= MAX_SIZE
149
+
150
+ create_container(url, token, container) rescue nil
151
+
152
+ file_name, file_mtime, file_size = file_info(file_path)
153
+
154
+ if file_size > options[:segments_size]
155
+ create_container(url, token, "#{container}_segments") rescue nil
156
+
157
+ segments_minus_one = file_size / options[:segments_size]
158
+ last_piece = file_size - segments_minus_one * options[:segments_size]
159
+ segments_minus_one.times do |segment|
160
+ upload_segment(
161
+ url, token, "#{container}_segments", file_path,
162
+ :size => options[:segments_size],
163
+ :position => options[:segments_size] * segment,
164
+ :object_name => upload_path_for(file_path, segment)
165
+ )
166
+ end
167
+
168
+ upload_segment(
169
+ url, token, "#{container}_segments", file_path,
170
+ :size => last_piece,
171
+ :position => options[:segments_size] * segments_minus_one,
172
+ :object_name => upload_path_for(file_path, segments_minus_one)
173
+ )
174
+
175
+ create_manifest(url, token, container, file_path)
176
+ else
177
+ upload_segment(url, token, container, file_path)
178
+ end
179
+ end
180
+
181
+ # Uploads a given object to a given container
182
+ def upload_segment(url, token, container, file_path, options={})
126
183
  options[:object_name] ||= file_path.match(/.+\/(.+?)$/)[1]
127
184
  file = File.open(file_path, "rb")
128
185
 
@@ -142,6 +199,25 @@ module Openstack
142
199
  ensure
143
200
  file.close rescue nil
144
201
  end
202
+
203
+ private
204
+ # Returns the standard swift path for a given file path and segment
205
+ def upload_path_for(file_path, segment)
206
+ "%s/%s/%s/%08d" % (file_info(file_path) << segment)
207
+ end
208
+
209
+ # Get relevant informations about a file
210
+ # Returns an array with:
211
+ # file_name
212
+ # file_mtime
213
+ # file_size
214
+ def file_info(file_path)
215
+ [
216
+ file_path.match(/.+\/(.+?)$/)[1],
217
+ File.mtime(file_path).to_f.round(2),
218
+ File.size(file_path)
219
+ ]
220
+ end
145
221
  end
146
222
  end
147
223
  end
@@ -2,9 +2,6 @@
2
2
  module Openstack
3
3
  module Swift
4
4
  class Client
5
- SWIFT_API = Openstack::Swift::Api
6
- MAX_SIZE = 4 * 1024 ** 3
7
-
8
5
  # Initialize method
9
6
  # It uses the authenticate method to store the tokens for future requests
10
7
  def initialize(proxy, user, password)
@@ -17,7 +14,7 @@ module Openstack
17
14
  # avoiding to request a new token for each request
18
15
  # It should be used to force a new token
19
16
  def authenticate!
20
- @url, _, @token = SWIFT_API.auth(@proxy, @user, @password)
17
+ @url, _, @token = Api.auth(@proxy, @user, @password)
21
18
 
22
19
  if @url.blank? or @token.blank?
23
20
  raise AuthenticationError
@@ -31,7 +28,7 @@ module Openstack
31
28
  # object_count: Number of objects that this account have allocated
32
29
  # container_count: Number of container
33
30
  def account_info
34
- headers = SWIFT_API.account(@url, @token)
31
+ headers = Api.account(@url, @token)
35
32
  {
36
33
  "bytes_used" => headers["x-account-bytes-used"],
37
34
  "object_count" => headers["x-account-object-count"],
@@ -46,7 +43,7 @@ module Openstack
46
43
  # manifest
47
44
  # content_length
48
45
  def object_info(container, object)
49
- headers = SWIFT_API.object_stat(@url, @token, container, object)
46
+ headers = Api.object_stat(@url, @token, container, object)
50
47
  {
51
48
  "last_modified" => headers["last-modified"],
52
49
  "md5" => headers["etag"],
@@ -58,63 +55,21 @@ module Openstack
58
55
 
59
56
  # This method uploads a file from a given to a given container
60
57
  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
58
+ Api.upload_object(@url, @token, container, file_path, options)
93
59
  end
94
60
 
95
61
  # This method downloads a object from a given container
96
62
  def download(container, object)
97
- SWIFT_API.download_object(@url, @token, container, object)
63
+ Api.download_object(@url, @token, container, object)
98
64
  end
99
65
 
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
- ]
66
+ # Delete a given object from a given container
67
+ def delete(container, object)
68
+ if object_info(container, object)["manifest"]
69
+ Api.delete_objects_from_manifest(@url, @token, container, object)
70
+ else
71
+ Api.delete_object(@url, @token, container, object)
72
+ end
118
73
  end
119
74
  end
120
75
  end
@@ -1,4 +1,5 @@
1
1
  # -*- coding: UTF-8 -*-
2
2
  module Openstack::Swift
3
3
  class AuthenticationError < Exception; end
4
+ class ObjectNotFoundError < Exception; end
4
5
  end
@@ -1,6 +1,6 @@
1
1
  # -*- coding: UTF-8 -*-
2
2
  module Openstack
3
3
  module Swift
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.0"
5
5
  end
6
6
  end
@@ -33,7 +33,7 @@ describe Openstack::Swift::Api do
33
33
  end
34
34
 
35
35
  context "when authenticated" do
36
- let!(:swift_dummy_file){ File.open("/tmp/swift-dummy", "w") {|f| f.puts("test file"*1000)} }
36
+ let!(:swift_dummy_file){ File.open("/tmp/swift-dummy", "w") {|f| f.puts("test file"*2000)}; "/tmp/swift-dummy" }
37
37
 
38
38
  before do
39
39
  @url, _, @token = subject.auth(
@@ -70,10 +70,31 @@ describe Openstack::Swift::Api do
70
70
  subject.create_container(@url, @token, "pothix_container").should be_true
71
71
  end
72
72
 
73
+ context "whe excluding an object" do
74
+ before { @container = "morellon" }
75
+
76
+ it "should delete an existent object" do
77
+ subject.upload_object(@url, @token, @container, "/tmp/swift-dummy").should be_true
78
+ subject.delete_object(@url, @token, @container, "swift-dummy").should be_true
79
+ end
80
+ end
81
+
82
+ context "when excluding all objects from a manifest" do
83
+ it "should remove all objects from a given manifest" do
84
+ container = "multiseg"
85
+ subject.upload_object(@url, @token, container, swift_dummy_file, {:segments_size => 1024})
86
+ manifest = subject.object_stat(@url, @token, container, "swift-dummy")["x-object-manifest"]
87
+ segment_path = manifest.split("/")[1..-1].join("/") + "/00000000"
88
+ subject.object_stat(@url, @token, "#{container}_segments", segment_path)["etag"].should_not be_nil
89
+ subject.delete_objects_from_manifest(@url, @token, container, "swift-dummy").should be_true
90
+ subject.object_stat(@url, @token, "#{container}_segments", segment_path)["etag"].should be_nil
91
+ end
92
+ end
93
+
73
94
  context "when excluding a container" do
74
95
  before { @container = "pothix_container" }
75
96
 
76
- it "should delete a existent container" do
97
+ it "should delete an existent container" do
77
98
  subject.create_container(@url, @token, @container).should be_true
78
99
  subject.delete_container(@url, @token, @container).should be_true
79
100
  end
@@ -25,10 +25,11 @@ describe "Openstack::Swift::Client" do
25
25
  context "when authenticated" do
26
26
  subject { Openstack::Swift::Client.new(Openstack::SwiftConfig[:url], Openstack::SwiftConfig[:user], Openstack::SwiftConfig[:pass]) }
27
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
28
+ it "should try to upload" do
29
+ expect {
30
+ subject.upload("pothix", swift_dummy_file, {:segments_size => 1024*2})
31
+ }.to_not raise_error
32
+ subject.object_info("pothix", "swift-dummy")["manifest"].should_not be_nil
32
33
  end
33
34
 
34
35
  it "should download an splitted file" do
@@ -43,5 +44,19 @@ describe "Openstack::Swift::Client" do
43
44
  account_info.should have_key("object_count")
44
45
  account_info.should have_key("container_count")
45
46
  end
47
+
48
+ context "when deleting" do
49
+ it "should call the delete method for a non manifest file" do
50
+ Openstack::Swift::Api.should_receive(:object_stat).and_return({"x-object-manifest" => nil})
51
+ Openstack::Swift::Api.should_receive(:delete_object)
52
+ subject.delete("pothix","swift-dummy")
53
+ end
54
+
55
+ it "should call the delete_objects_from_manifest method for a manifest file" do
56
+ Openstack::Swift::Api.should_receive(:object_stat).and_return({"x-object-manifest" => "pothix_segments/swift-dummy/1313763802.0/9001/"})
57
+ Openstack::Swift::Api.should_receive(:delete_objects_from_manifest)
58
+ subject.delete("pothix","swift-dummy")
59
+ end
60
+ end
46
61
  end
47
62
  end