aws-sdk 1.2.3 → 1.2.4
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/lib/aws/core.rb +1 -1
- data/lib/aws/core/http/net_http_handler.rb +21 -62
- data/lib/aws/record/attribute_macros.rb +28 -4
- data/lib/aws/record/attributes/date.rb +89 -0
- data/lib/aws/record/base.rb +41 -4
- data/lib/aws/record/finder_methods.rb +7 -5
- data/lib/aws/record/scope.rb +32 -5
- data/lib/aws/s3/bucket.rb +14 -5
- data/lib/aws/s3/bucket_version_collection.rb +7 -11
- data/lib/aws/s3/client.rb +37 -1
- data/lib/aws/s3/client/xml.rb +8 -1
- data/lib/aws/s3/errors.rb +14 -0
- data/lib/aws/s3/multipart_upload_collection.rb +0 -3
- data/lib/aws/s3/object_collection.rb +182 -6
- data/lib/aws/s3/paginated_collection.rb +21 -40
- data/lib/aws/s3/prefix_and_delimiter_collection.rb +0 -7
- data/lib/aws/s3/presigned_post.rb +26 -11
- data/lib/aws/s3/request.rb +1 -1
- data/lib/aws/s3/s3_object.rb +10 -4
- data/lib/aws/s3/uploaded_part_collection.rb +3 -1
- data/lib/net/http/connection_pool.rb +193 -0
- data/lib/net/http/connection_pool/connection.rb +132 -0
- data/lib/net/http/connection_pool/session.rb +93 -0
- metadata +8 -4
data/lib/aws/s3/bucket.rb
CHANGED
@@ -102,19 +102,28 @@ module AWS
|
|
102
102
|
|
103
103
|
# Deletes the current bucket.
|
104
104
|
#
|
105
|
-
# @note the bucket
|
105
|
+
# @note This operation will fail if the bucket is not empty.
|
106
|
+
#
|
106
107
|
# @return [nil]
|
108
|
+
#
|
107
109
|
def delete
|
108
110
|
client.delete_bucket(:bucket_name => @name)
|
109
111
|
nil
|
110
112
|
end
|
111
113
|
|
112
|
-
#
|
113
|
-
# then
|
114
|
+
# Attempts to delete all objects (or object versions) from the bucket
|
115
|
+
# and then attempts to delete the bucket.
|
116
|
+
#
|
117
|
+
# @note This operation may fail if you do not have privileges to delete
|
118
|
+
# all objects from the bucket.
|
119
|
+
#
|
114
120
|
# @return [nil]
|
121
|
+
#
|
115
122
|
def delete!
|
116
|
-
versions.
|
117
|
-
|
123
|
+
versions.each_batch do |versions|
|
124
|
+
objects.delete(versions)
|
125
|
+
end
|
126
|
+
self.delete
|
118
127
|
end
|
119
128
|
|
120
129
|
# @return [String] bucket owner id
|
@@ -19,8 +19,6 @@ module AWS
|
|
19
19
|
# @see PrefixedCollection
|
20
20
|
class BucketVersionCollection
|
21
21
|
|
22
|
-
include Core::Model
|
23
|
-
include Enumerable
|
24
22
|
include PrefixAndDelimiterCollection
|
25
23
|
|
26
24
|
# @param [Bucket] bucket
|
@@ -35,14 +33,18 @@ module AWS
|
|
35
33
|
# @return [ObjectVersion] Returns the most recently created object
|
36
34
|
# version in the entire bucket.
|
37
35
|
def latest
|
38
|
-
|
36
|
+
first
|
37
|
+
#self.find{|version| true }
|
39
38
|
end
|
40
39
|
|
41
40
|
# Yields once for each version in the bucket.
|
42
41
|
#
|
43
42
|
# @yield [object_version]
|
43
|
+
#
|
44
44
|
# @yieldparam [ObjectVersion] object_version
|
45
|
+
#
|
45
46
|
# @return nil
|
47
|
+
#
|
46
48
|
def each options = {}, █ super; end
|
47
49
|
|
48
50
|
# @private
|
@@ -50,10 +52,8 @@ module AWS
|
|
50
52
|
def each_member_in_page(page, &block)
|
51
53
|
super
|
52
54
|
page.versions.each do |version|
|
53
|
-
object_version =
|
54
|
-
|
55
|
-
version.version_id,
|
56
|
-
:delete_marker => version.delete_marker?)
|
55
|
+
object_version = ObjectVersion.new(bucket.objects[version.key],
|
56
|
+
version.version_id, :delete_marker => version.delete_marker?)
|
57
57
|
yield(object_version)
|
58
58
|
end
|
59
59
|
end
|
@@ -72,10 +72,6 @@ module AWS
|
|
72
72
|
protected
|
73
73
|
def pagination_markers; super + [:version_id_marker]; end
|
74
74
|
|
75
|
-
# @private
|
76
|
-
protected
|
77
|
-
def page_size(resp); super + resp.versions.size; end
|
78
|
-
|
79
75
|
end
|
80
76
|
end
|
81
77
|
end
|
data/lib/aws/s3/client.rb
CHANGED
@@ -15,6 +15,7 @@ require 'rexml/document'
|
|
15
15
|
require 'pathname'
|
16
16
|
require 'stringio'
|
17
17
|
require 'json'
|
18
|
+
require 'digest/md5'
|
18
19
|
|
19
20
|
module AWS
|
20
21
|
class S3
|
@@ -561,17 +562,19 @@ module AWS
|
|
561
562
|
# marker.
|
562
563
|
object_method(:get_object, :get,
|
563
564
|
:header_options => {
|
564
|
-
:range => "Range",
|
565
565
|
:if_modified_since => "If-Modified-Since",
|
566
566
|
:if_unmodified_since => "If-Unmodified-Since",
|
567
567
|
:if_match => "If-Match",
|
568
568
|
:if_none_match => "If-None-Match"
|
569
569
|
}) do
|
570
570
|
configure_request do |req, options|
|
571
|
+
|
571
572
|
super(req, options)
|
573
|
+
|
572
574
|
if options[:version_id]
|
573
575
|
req.add_param('versionId', options[:version_id])
|
574
576
|
end
|
577
|
+
|
575
578
|
["If-Modified-Since",
|
576
579
|
"If-Unmodified-Since"].each do |date_header|
|
577
580
|
case value = req.headers[date_header]
|
@@ -581,6 +584,13 @@ module AWS
|
|
581
584
|
req.headers[date_header] = value.rfc2822
|
582
585
|
end
|
583
586
|
end
|
587
|
+
|
588
|
+
if options[:range]
|
589
|
+
range = options[:range]
|
590
|
+
range = "bytes=#{range.first}-#{range.last}" if range.is_a?(Range)
|
591
|
+
req.headers['Range'] = range
|
592
|
+
end
|
593
|
+
|
584
594
|
end
|
585
595
|
|
586
596
|
process_response do |resp|
|
@@ -705,6 +715,31 @@ module AWS
|
|
705
715
|
end
|
706
716
|
end
|
707
717
|
|
718
|
+
bucket_method(:delete_objects, :post, 'delete', XML::DeleteObjects) do
|
719
|
+
configure_request do |req, options|
|
720
|
+
|
721
|
+
super(req, options)
|
722
|
+
|
723
|
+
quiet = options.key?(:quiet) ? options[:quiet] : true
|
724
|
+
|
725
|
+
objects = options[:objects].inject('') do |xml,o|
|
726
|
+
xml << "<Object><Key>#{o[:key]}</Key>"
|
727
|
+
xml << "<VersionId>#{o[:version_id]}</VersionId>" if o[:version_id]
|
728
|
+
xml << "</Object>"
|
729
|
+
end
|
730
|
+
|
731
|
+
xml = '<?xml version="1.0" encoding="UTF-8"?>'
|
732
|
+
xml << "<Delete><Quiet>#{quiet}</Quiet>#{objects}</Delete>"
|
733
|
+
|
734
|
+
req.body = xml
|
735
|
+
|
736
|
+
md5 = Base64.encode64(Digest::MD5.digest(xml)).strip
|
737
|
+
|
738
|
+
req.headers['content-md5'] = md5
|
739
|
+
|
740
|
+
end
|
741
|
+
end
|
742
|
+
|
708
743
|
object_method(:upload_part, :put,
|
709
744
|
:header_options => {
|
710
745
|
:content_md5 => 'Content-MD5'
|
@@ -799,6 +834,7 @@ module AWS
|
|
799
834
|
object_method(:copy_object, :put,
|
800
835
|
:header_options => {
|
801
836
|
:copy_source => 'x-amz-copy-source',
|
837
|
+
:cache_control => 'Cache-Control',
|
802
838
|
:metadata_directive => 'x-amz-metadata-directive',
|
803
839
|
:storage_class => 'x-amz-storage-class',
|
804
840
|
:server_side_encryption => 'x-amz-server-side-encryption',
|
data/lib/aws/s3/client/xml.rb
CHANGED
@@ -159,10 +159,17 @@ module AWS
|
|
159
159
|
element("StorageClass") { symbol_value }
|
160
160
|
element("Initiated") { datetime_value }
|
161
161
|
end
|
162
|
-
|
163
162
|
include HasCommonPrefixes
|
164
163
|
end
|
165
164
|
|
165
|
+
DeleteObjects = Core::XmlGrammar.customize do
|
166
|
+
element("Deleted") do
|
167
|
+
element("DeleteMarker") { boolean_value }
|
168
|
+
list
|
169
|
+
end
|
170
|
+
element("Error") { list; rename('errors') }
|
171
|
+
end
|
172
|
+
|
166
173
|
# keep default behavior
|
167
174
|
CompleteMultipartUpload = Core::XmlGrammar.customize
|
168
175
|
|
data/lib/aws/s3/errors.rb
CHANGED
@@ -31,6 +31,20 @@ module AWS
|
|
31
31
|
# @private
|
32
32
|
module Errors
|
33
33
|
|
34
|
+
class BatchDeleteError < StandardError
|
35
|
+
|
36
|
+
def initialize error_counts
|
37
|
+
@error_counts = error_counts
|
38
|
+
total = error_counts.values.inject(0) {|sum,count| sum + count }
|
39
|
+
super("Failed to delete #{total} objects")
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Hash] Returns a hash of error codes and how many
|
43
|
+
# objects failed with that code.
|
44
|
+
attr_reader :error_counts
|
45
|
+
|
46
|
+
end
|
47
|
+
|
34
48
|
# This error is special, because S3 does not (and must not
|
35
49
|
# according to RFC 2616) return a body with the HTTP response.
|
36
50
|
# The interface is the same as for any other client error.
|
@@ -102,6 +102,153 @@ module AWS
|
|
102
102
|
super(prefix, mode)
|
103
103
|
end
|
104
104
|
|
105
|
+
# Deletes the objects provided in as few requests as possible.
|
106
|
+
#
|
107
|
+
# # delete 2 objects (by key) in a single request
|
108
|
+
# bucket.objects.delete('abc', 'xyz')
|
109
|
+
#
|
110
|
+
# You can delete objects also by passing their S3Object representation:
|
111
|
+
#
|
112
|
+
# to_delete = []
|
113
|
+
# to_delete << buckets.objects['foo']
|
114
|
+
# to_delete << buckets.objects['bar']
|
115
|
+
#
|
116
|
+
# bucket.objects.delete(to_delete)
|
117
|
+
#
|
118
|
+
# @param [Mixed] objects One or more objects to delete. Each object
|
119
|
+
# can be one of the following:
|
120
|
+
#
|
121
|
+
# * An object key (string)
|
122
|
+
# * A hash with :key and :version_id (for versioned objects)
|
123
|
+
# * An {S3Object} instance
|
124
|
+
# * An {ObjectVersion} instance
|
125
|
+
#
|
126
|
+
# @raise [BatchDeleteError] If any of the objects failed to delete,
|
127
|
+
# a BatchDeleteError will be raised with a summary of the errors.
|
128
|
+
#
|
129
|
+
# @return [nil]
|
130
|
+
#
|
131
|
+
def delete *objects
|
132
|
+
|
133
|
+
objects = objects.flatten.collect do |obj|
|
134
|
+
case obj
|
135
|
+
when String then { :key => obj }
|
136
|
+
when Hash then obj
|
137
|
+
when S3Object then { :key => obj.key }
|
138
|
+
when ObjectVersion then { :key => obj.key, :version_id => obj.version_id }
|
139
|
+
else
|
140
|
+
msg = "objects must be keys (strings or hashes with :key and " +
|
141
|
+
":version_id), S3Objects or ObjectVersions, got " +
|
142
|
+
object.class.name
|
143
|
+
raise ArgumentError, msg
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
batch_helper = BatchHelper.new(1000) do |batch|
|
148
|
+
client_opts = {}
|
149
|
+
client_opts[:bucket_name] = bucket.name
|
150
|
+
client_opts[:quiet] = true
|
151
|
+
client_opts[:objects] = batch
|
152
|
+
client.delete_objects(client_opts)
|
153
|
+
end
|
154
|
+
|
155
|
+
error_counts = {}
|
156
|
+
batch_helper.after_batch do |response|
|
157
|
+
response.errors.each do |error|
|
158
|
+
error_counts[error.code] ||= 0
|
159
|
+
error_counts[error.code] += 1
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
objects.each do |object|
|
164
|
+
batch_helper.add(object)
|
165
|
+
end
|
166
|
+
|
167
|
+
batch_helper.complete!
|
168
|
+
|
169
|
+
raise Errors::BatchDeleteError.new(error_counts) unless
|
170
|
+
error_counts.empty?
|
171
|
+
|
172
|
+
nil
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
# Deletes each object in the collection that returns a true value
|
177
|
+
# from block passed to this method. Deletes are batched for efficiency.
|
178
|
+
#
|
179
|
+
# # delete text files in the 2009 "folder"
|
180
|
+
# bucket.objects.with_prefix('2009/').delete_if {|o| o.key =~ /\.txt$/ }
|
181
|
+
#
|
182
|
+
# @yieldparam [S3Object] object
|
183
|
+
#
|
184
|
+
# @raise [BatchDeleteError] If any of the objects failed to delete,
|
185
|
+
# a BatchDeleteError will be raised with a summary of the errors.
|
186
|
+
#
|
187
|
+
def delete_if &block
|
188
|
+
|
189
|
+
error_counts = {}
|
190
|
+
|
191
|
+
batch_helper = BatchHelper.new(1000) do |objects|
|
192
|
+
begin
|
193
|
+
delete(objects)
|
194
|
+
rescue Errors::BatchDeleteError => error
|
195
|
+
error.error_counts.each_pair do |code,count|
|
196
|
+
error_counts[code] ||= 0
|
197
|
+
error_counts[code] += count
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
each do |object|
|
203
|
+
batch_helper.add(object) if yield(object)
|
204
|
+
end
|
205
|
+
|
206
|
+
batch_helper.complete!
|
207
|
+
|
208
|
+
raise Errors::BatchDeleteError.new(error_counts) unless
|
209
|
+
error_counts.empty?
|
210
|
+
|
211
|
+
nil
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
# Deletes all objects represented by this collection.
|
216
|
+
#
|
217
|
+
# @example Delete all objects from a bucket
|
218
|
+
#
|
219
|
+
# bucket.objects.delete_all
|
220
|
+
#
|
221
|
+
# @example Delete objects with a given prefix
|
222
|
+
#
|
223
|
+
# bucket.objects.with_prefix('2009/').delete_all
|
224
|
+
#
|
225
|
+
# @raise [BatchDeleteError] If any of the objects failed to delete,
|
226
|
+
# a BatchDeleteError will be raised with a summary of the errors.
|
227
|
+
#
|
228
|
+
# @return [Array] Returns an array of results
|
229
|
+
#
|
230
|
+
def delete_all
|
231
|
+
|
232
|
+
error_counts = {}
|
233
|
+
|
234
|
+
each_batch do |objects|
|
235
|
+
begin
|
236
|
+
delete(objects)
|
237
|
+
rescue Errors::BatchDeleteError => error
|
238
|
+
error.error_counts.each_pair do |code,count|
|
239
|
+
error_counts[code] ||= 0
|
240
|
+
error_counts[code] += count
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
raise Errors::BatchDeleteError.new(error_counts) unless
|
246
|
+
error_counts.empty?
|
247
|
+
|
248
|
+
nil
|
249
|
+
|
250
|
+
end
|
251
|
+
|
105
252
|
# Iterates the collection, yielding instances of S3Object.
|
106
253
|
#
|
107
254
|
# Use break or raise an exception to terminate the enumeration.
|
@@ -127,7 +274,7 @@ module AWS
|
|
127
274
|
|
128
275
|
# @private
|
129
276
|
protected
|
130
|
-
def list_request
|
277
|
+
def list_request options
|
131
278
|
client.list_objects(options)
|
132
279
|
end
|
133
280
|
|
@@ -139,14 +286,43 @@ module AWS
|
|
139
286
|
|
140
287
|
# @private
|
141
288
|
protected
|
142
|
-
def
|
143
|
-
|
289
|
+
def next_markers page
|
290
|
+
{ :marker => (last = page.contents.last and last.key) }
|
144
291
|
end
|
145
292
|
|
293
|
+
# processes items in batches of 1k items
|
146
294
|
# @private
|
147
|
-
|
148
|
-
|
149
|
-
|
295
|
+
class BatchHelper
|
296
|
+
|
297
|
+
def initialize batch_size, &block
|
298
|
+
@batch_size = batch_size
|
299
|
+
@block = block
|
300
|
+
@batch = []
|
301
|
+
end
|
302
|
+
|
303
|
+
def after_batch &block
|
304
|
+
@after_batch = block
|
305
|
+
end
|
306
|
+
|
307
|
+
def add item
|
308
|
+
@batch << item
|
309
|
+
if @batch.size == @batch_size
|
310
|
+
process_batch
|
311
|
+
@batch = []
|
312
|
+
end
|
313
|
+
item
|
314
|
+
end
|
315
|
+
|
316
|
+
def complete!
|
317
|
+
process_batch unless @batch.empty?
|
318
|
+
end
|
319
|
+
|
320
|
+
protected
|
321
|
+
def process_batch
|
322
|
+
response = @block.call(@batch)
|
323
|
+
@after_batch.call(response) if @after_batch
|
324
|
+
end
|
325
|
+
|
150
326
|
end
|
151
327
|
|
152
328
|
end
|
@@ -17,47 +17,33 @@ module AWS
|
|
17
17
|
# @private
|
18
18
|
module PaginatedCollection
|
19
19
|
|
20
|
-
|
21
|
-
each_page(options) do |page|
|
22
|
-
each_member_in_page(page, &block)
|
23
|
-
end
|
24
|
-
nil
|
25
|
-
end
|
20
|
+
include Core::Collection::Limitable
|
26
21
|
|
27
22
|
protected
|
28
|
-
def
|
23
|
+
def _each_item markers, limit, options = {}, &block
|
24
|
+
|
25
|
+
options = list_options(options)
|
26
|
+
options.merge!(markers) unless markers.nil? or markers.empty?
|
27
|
+
options[limit_param] = limit || 1000
|
28
|
+
|
29
|
+
response = list_request(options)
|
30
|
+
|
31
|
+
each_member_in_page(response, &block)
|
32
|
+
|
33
|
+
response.truncated? ? next_markers(response) : nil
|
29
34
|
|
30
|
-
protected
|
31
|
-
def each_page(options = {}, &block)
|
32
|
-
opts = list_options(options)
|
33
|
-
limit = options[:limit]
|
34
|
-
batch_size = options[:batch_size] || 1000
|
35
|
-
markers = {}
|
36
|
-
received = 0
|
37
|
-
|
38
|
-
loop do
|
39
|
-
page_opts = opts.dup
|
40
|
-
page_opts.merge!(markers)
|
41
|
-
page_opts[limit_param] =
|
42
|
-
limit ? [limit - received, batch_size].min : batch_size
|
43
|
-
|
44
|
-
page = list_request(page_opts)
|
45
|
-
markers = next_markers(page)
|
46
|
-
received += page_size(page)
|
47
|
-
|
48
|
-
yield(page)
|
49
|
-
|
50
|
-
return unless page.truncated?
|
51
|
-
end
|
52
35
|
end
|
53
36
|
|
37
|
+
protected
|
38
|
+
def each_member_in_page(page, &block); end
|
39
|
+
|
54
40
|
protected
|
55
41
|
def list_request(options)
|
56
42
|
raise NotImplementedError
|
57
43
|
end
|
58
44
|
|
59
45
|
protected
|
60
|
-
def list_options
|
46
|
+
def list_options options
|
61
47
|
opts = {}
|
62
48
|
opts[:bucket_name] = bucket.name if respond_to?(:bucket)
|
63
49
|
opts
|
@@ -74,20 +60,15 @@ module AWS
|
|
74
60
|
end
|
75
61
|
|
76
62
|
protected
|
77
|
-
def next_markers
|
78
|
-
pagination_markers.inject({}) do |markers,
|
79
|
-
|
63
|
+
def next_markers page
|
64
|
+
pagination_markers.inject({}) do |markers, marker_name|
|
65
|
+
if marker = page.send("next_#{marker_name}")
|
66
|
+
markers[marker_name] = marker if marker
|
67
|
+
end
|
80
68
|
markers
|
81
69
|
end
|
82
70
|
end
|
83
71
|
|
84
|
-
protected
|
85
|
-
def page_size(resp)
|
86
|
-
(resp.respond_to?(:common_prefixes) and
|
87
|
-
prefixes = resp.common_prefixes and
|
88
|
-
prefixes.size) or 0
|
89
|
-
end
|
90
|
-
|
91
72
|
end
|
92
73
|
|
93
74
|
end
|