aws-sdk-s3 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/lib/aws-sdk-s3.rb +66 -0
  3. data/lib/aws-sdk-s3/bucket.rb +595 -0
  4. data/lib/aws-sdk-s3/bucket_acl.rb +168 -0
  5. data/lib/aws-sdk-s3/bucket_cors.rb +146 -0
  6. data/lib/aws-sdk-s3/bucket_lifecycle.rb +164 -0
  7. data/lib/aws-sdk-s3/bucket_logging.rb +142 -0
  8. data/lib/aws-sdk-s3/bucket_notification.rb +187 -0
  9. data/lib/aws-sdk-s3/bucket_policy.rb +138 -0
  10. data/lib/aws-sdk-s3/bucket_region_cache.rb +79 -0
  11. data/lib/aws-sdk-s3/bucket_request_payment.rb +128 -0
  12. data/lib/aws-sdk-s3/bucket_tagging.rb +143 -0
  13. data/lib/aws-sdk-s3/bucket_versioning.rb +188 -0
  14. data/lib/aws-sdk-s3/bucket_website.rb +177 -0
  15. data/lib/aws-sdk-s3/client.rb +3171 -0
  16. data/lib/aws-sdk-s3/client_api.rb +1991 -0
  17. data/lib/aws-sdk-s3/customizations.rb +29 -0
  18. data/lib/aws-sdk-s3/customizations/bucket.rb +127 -0
  19. data/lib/aws-sdk-s3/customizations/multipart_upload.rb +42 -0
  20. data/lib/aws-sdk-s3/customizations/object.rb +257 -0
  21. data/lib/aws-sdk-s3/customizations/object_summary.rb +65 -0
  22. data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +11 -0
  23. data/lib/aws-sdk-s3/encryption.rb +19 -0
  24. data/lib/aws-sdk-s3/encryption/client.rb +369 -0
  25. data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +178 -0
  26. data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +63 -0
  27. data/lib/aws-sdk-s3/encryption/default_key_provider.rb +38 -0
  28. data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +50 -0
  29. data/lib/aws-sdk-s3/encryption/errors.rb +13 -0
  30. data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +50 -0
  31. data/lib/aws-sdk-s3/encryption/io_decrypter.rb +29 -0
  32. data/lib/aws-sdk-s3/encryption/io_encrypter.rb +69 -0
  33. data/lib/aws-sdk-s3/encryption/key_provider.rb +29 -0
  34. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +71 -0
  35. data/lib/aws-sdk-s3/encryption/materials.rb +58 -0
  36. data/lib/aws-sdk-s3/encryption/utils.rb +79 -0
  37. data/lib/aws-sdk-s3/errors.rb +23 -0
  38. data/lib/aws-sdk-s3/file_part.rb +75 -0
  39. data/lib/aws-sdk-s3/file_uploader.rb +58 -0
  40. data/lib/aws-sdk-s3/legacy_signer.rb +186 -0
  41. data/lib/aws-sdk-s3/multipart_file_uploader.rb +187 -0
  42. data/lib/aws-sdk-s3/multipart_upload.rb +287 -0
  43. data/lib/aws-sdk-s3/multipart_upload_error.rb +16 -0
  44. data/lib/aws-sdk-s3/multipart_upload_part.rb +314 -0
  45. data/lib/aws-sdk-s3/object.rb +942 -0
  46. data/lib/aws-sdk-s3/object_acl.rb +214 -0
  47. data/lib/aws-sdk-s3/object_copier.rb +99 -0
  48. data/lib/aws-sdk-s3/object_multipart_copier.rb +179 -0
  49. data/lib/aws-sdk-s3/object_summary.rb +794 -0
  50. data/lib/aws-sdk-s3/object_version.rb +406 -0
  51. data/lib/aws-sdk-s3/plugins/accelerate.rb +92 -0
  52. data/lib/aws-sdk-s3/plugins/bucket_dns.rb +89 -0
  53. data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +23 -0
  54. data/lib/aws-sdk-s3/plugins/dualstack.rb +70 -0
  55. data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +29 -0
  56. data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +23 -0
  57. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +47 -0
  58. data/lib/aws-sdk-s3/plugins/location_constraint.rb +33 -0
  59. data/lib/aws-sdk-s3/plugins/md5s.rb +79 -0
  60. data/lib/aws-sdk-s3/plugins/redirects.rb +41 -0
  61. data/lib/aws-sdk-s3/plugins/s3_signer.rb +208 -0
  62. data/lib/aws-sdk-s3/plugins/sse_cpk.rb +68 -0
  63. data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +94 -0
  64. data/lib/aws-sdk-s3/presigned_post.rb +647 -0
  65. data/lib/aws-sdk-s3/presigner.rb +160 -0
  66. data/lib/aws-sdk-s3/resource.rb +96 -0
  67. data/lib/aws-sdk-s3/types.rb +5750 -0
  68. data/lib/aws-sdk-s3/waiters.rb +178 -0
  69. metadata +154 -0
@@ -0,0 +1,187 @@
1
+ require 'pathname'
2
+ require 'thread'
3
+ require 'set'
4
+
5
+ module Aws
6
+ module S3
7
+ # @api private
8
+ class MultipartFileUploader
9
+
10
+ MIN_PART_SIZE = 5 * 1024 * 1024 # 5MB
11
+
12
+ FILE_TOO_SMALL = "unable to multipart upload files smaller than 5MB"
13
+
14
+ MAX_PARTS = 10_000
15
+
16
+ THREAD_COUNT = 10
17
+
18
+ # @api private
19
+ CREATE_OPTIONS =
20
+ Set.new(Client.api.operation(:create_multipart_upload).input.shape.member_names)
21
+
22
+ # @api private
23
+ UPLOAD_PART_OPTIONS =
24
+ Set.new(Client.api.operation(:upload_part).input.shape.member_names)
25
+
26
+ # @option options [Client] :client
27
+ def initialize(options = {})
28
+ @client = options[:client] || Client.new
29
+ @thread_count = options[:thread_count] || THREAD_COUNT
30
+ end
31
+
32
+ # @return [Client]
33
+ attr_reader :client
34
+
35
+ # @param [String,Pathname,File,Tempfile] source
36
+ # @option options [required,String] :bucket
37
+ # @option options [required,String] :key
38
+ # @return [void]
39
+ def upload(source, options = {})
40
+ if File.size(source) < MIN_PART_SIZE
41
+ raise ArgumentError, FILE_TOO_SMALL
42
+ else
43
+ upload_id = initiate_upload(options)
44
+ parts = upload_parts(upload_id, source, options)
45
+ complete_upload(upload_id, parts, options)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def initiate_upload(options)
52
+ @client.create_multipart_upload(create_opts(options)).upload_id
53
+ end
54
+
55
+ def complete_upload(upload_id, parts, options)
56
+ @client.complete_multipart_upload(
57
+ bucket: options[:bucket],
58
+ key: options[:key],
59
+ upload_id: upload_id,
60
+ multipart_upload: { parts: parts })
61
+ end
62
+
63
+ def upload_parts(upload_id, source, options)
64
+ pending = PartList.new(compute_parts(upload_id, source, options))
65
+ completed = PartList.new
66
+ errors = upload_in_threads(pending, completed)
67
+ if errors.empty?
68
+ completed.to_a.sort_by { |part| part[:part_number] }
69
+ else
70
+ abort_upload(upload_id, options, errors)
71
+ end
72
+ end
73
+
74
+ def abort_upload(upload_id, options, errors)
75
+ @client.abort_multipart_upload(
76
+ bucket: options[:bucket],
77
+ key: options[:key],
78
+ upload_id: upload_id
79
+ )
80
+ msg = "multipart upload failed: #{errors.map(&:message).join("; ")}"
81
+ raise MultipartUploadError.new(msg, errors)
82
+ rescue MultipartUploadError => error
83
+ raise error
84
+ rescue => error
85
+ msg = "failed to abort multipart upload: #{error.message}"
86
+ raise MultipartUploadError.new(msg, errors + [error])
87
+ end
88
+
89
+ def compute_parts(upload_id, source, options)
90
+ size = File.size(source)
91
+ default_part_size = compute_default_part_size(size)
92
+ offset = 0
93
+ part_number = 1
94
+ parts = []
95
+ while offset < size
96
+ parts << upload_part_opts(options).merge({
97
+ upload_id: upload_id,
98
+ part_number: part_number,
99
+ body: FilePart.new(
100
+ source: source,
101
+ offset: offset,
102
+ size: part_size(size, default_part_size, offset)
103
+ )
104
+ })
105
+ part_number += 1
106
+ offset += default_part_size
107
+ end
108
+ parts
109
+ end
110
+
111
+ def create_opts(options)
112
+ CREATE_OPTIONS.inject({}) do |hash, key|
113
+ hash[key] = options[key] if options.key?(key)
114
+ hash
115
+ end
116
+ end
117
+
118
+ def upload_part_opts(options)
119
+ UPLOAD_PART_OPTIONS.inject({}) do |hash, key|
120
+ hash[key] = options[key] if options.key?(key)
121
+ hash
122
+ end
123
+ end
124
+
125
+ def upload_in_threads(pending, completed)
126
+ threads = []
127
+ @thread_count.times do
128
+ thread = Thread.new do
129
+ begin
130
+ while part = pending.shift
131
+ resp = @client.upload_part(part)
132
+ part[:body].close
133
+ completed.push(etag: resp.etag, part_number: part[:part_number])
134
+ end
135
+ nil
136
+ rescue => error
137
+ # keep other threads from uploading other parts
138
+ pending.clear!
139
+ error
140
+ end
141
+ end
142
+ thread.abort_on_exception = true
143
+ threads << thread
144
+ end
145
+ threads.map(&:value).compact
146
+ end
147
+
148
+ def compute_default_part_size(source_size)
149
+ [(source_size.to_f / MAX_PARTS).ceil, MIN_PART_SIZE].max.to_i
150
+ end
151
+
152
+ def part_size(total_size, part_size, offset)
153
+ if offset + part_size > total_size
154
+ total_size - offset
155
+ else
156
+ part_size
157
+ end
158
+ end
159
+
160
+ # @api private
161
+ class PartList
162
+
163
+ def initialize(parts = [])
164
+ @parts = parts
165
+ @mutex = Mutex.new
166
+ end
167
+
168
+ def push(part)
169
+ @mutex.synchronize { @parts.push(part) }
170
+ end
171
+
172
+ def shift
173
+ @mutex.synchronize { @parts.shift }
174
+ end
175
+
176
+ def clear!
177
+ @mutex.synchronize { @parts.clear }
178
+ end
179
+
180
+ def to_a
181
+ @mutex.synchronize { @parts.dup }
182
+ end
183
+
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,287 @@
1
+ # WARNING ABOUT GENERATED CODE
2
+ #
3
+ # This file is generated. See the contributing for info on making contributions:
4
+ # https://github.com/aws/aws-sdk-ruby/blob/master/CONTRIBUTING.md
5
+ #
6
+ # WARNING ABOUT GENERATED CODE
7
+
8
+ module Aws
9
+ module S3
10
+ class MultipartUpload
11
+
12
+ extend Aws::Deprecations
13
+
14
+ # @overload def initialize(bucket_name, object_key, id, options = {})
15
+ # @param [String] bucket_name
16
+ # @param [String] object_key
17
+ # @param [String] id
18
+ # @option options [Client] :client
19
+ # @overload def initialize(options = {})
20
+ # @option options [required, String] :bucket_name
21
+ # @option options [required, String] :object_key
22
+ # @option options [required, String] :id
23
+ # @option options [Client] :client
24
+ def initialize(*args)
25
+ options = Hash === args.last ? args.pop.dup : {}
26
+ @bucket_name = extract_bucket_name(args, options)
27
+ @object_key = extract_object_key(args, options)
28
+ @id = extract_id(args, options)
29
+ @data = options.delete(:data)
30
+ @client = options.delete(:client) || Client.new(options)
31
+ end
32
+
33
+ # @!group Read-Only Attributes
34
+
35
+ # @return [String]
36
+ def bucket_name
37
+ @bucket_name
38
+ end
39
+
40
+ # @return [String]
41
+ def object_key
42
+ @object_key
43
+ end
44
+
45
+ # @return [String]
46
+ def id
47
+ @id
48
+ end
49
+
50
+ # Upload ID that identifies the multipart upload.
51
+ # @return [String]
52
+ def upload_id
53
+ data.upload_id
54
+ end
55
+
56
+ # Key of the object for which the multipart upload was initiated.
57
+ # @return [String]
58
+ def key
59
+ data.key
60
+ end
61
+
62
+ # Date and time at which the multipart upload was initiated.
63
+ # @return [Time]
64
+ def initiated
65
+ data.initiated
66
+ end
67
+
68
+ # The class of storage used to store the object.
69
+ # @return [String]
70
+ def storage_class
71
+ data.storage_class
72
+ end
73
+
74
+ # @return [Types::Owner]
75
+ def owner
76
+ data.owner
77
+ end
78
+
79
+ # Identifies who initiated the multipart upload.
80
+ # @return [Types::Initiator]
81
+ def initiator
82
+ data.initiator
83
+ end
84
+
85
+ # @!endgroup
86
+
87
+ # @return [Client]
88
+ def client
89
+ @client
90
+ end
91
+
92
+ # @raise [Errors::ResourceNotLoadable]
93
+ # @api private
94
+ def load
95
+ msg = "#load is not implemented, data only available via enumeration"
96
+ raise Errors::ResourceNotLoadable, msg
97
+ end
98
+ alias :reload :load
99
+
100
+ # @raise [Errors::ResourceNotLoadableError] Raises when {#data_loaded?} is `false`.
101
+ # @return [Types::MultipartUpload]
102
+ # Returns the data for this {MultipartUpload}.
103
+ def data
104
+ load unless @data
105
+ @data
106
+ end
107
+
108
+ # @return [Boolean]
109
+ # Returns `true` if this resource is loaded. Accessing attributes or
110
+ # {#data} on an unloaded resource will trigger a call to {#load}.
111
+ def data_loaded?
112
+ !!@data
113
+ end
114
+
115
+ # @!group Actions
116
+
117
+ # @example Request syntax with placeholder values
118
+ #
119
+ # multipart_upload.abort({
120
+ # request_payer: "requester", # accepts requester
121
+ # })
122
+ # @param [Hash] options ({})
123
+ # @option options [String] :request_payer
124
+ # Confirms that the requester knows that she or he will be charged for
125
+ # the request. Bucket owners need not specify this parameter in their
126
+ # requests. Documentation on downloading objects from requester pays
127
+ # buckets can be found at
128
+ # http://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectsinRequesterPaysBuckets.html
129
+ # @return [Types::AbortMultipartUploadOutput]
130
+ def abort(options = {})
131
+ options = options.merge(
132
+ bucket: @bucket_name,
133
+ key: @object_key,
134
+ upload_id: @id
135
+ )
136
+ resp = @client.abort_multipart_upload(options)
137
+ resp.data
138
+ end
139
+
140
+ # @example Request syntax with placeholder values
141
+ #
142
+ # object = multipart_upload.complete({
143
+ # multipart_upload: {
144
+ # parts: [
145
+ # {
146
+ # etag: "ETag",
147
+ # part_number: 1,
148
+ # },
149
+ # ],
150
+ # },
151
+ # request_payer: "requester", # accepts requester
152
+ # })
153
+ # @param [Hash] options ({})
154
+ # @option options [Types::CompletedMultipartUpload] :multipart_upload
155
+ # @option options [String] :request_payer
156
+ # Confirms that the requester knows that she or he will be charged for
157
+ # the request. Bucket owners need not specify this parameter in their
158
+ # requests. Documentation on downloading objects from requester pays
159
+ # buckets can be found at
160
+ # http://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectsinRequesterPaysBuckets.html
161
+ # @return [Object]
162
+ def complete(options = {})
163
+ options = options.merge(
164
+ bucket: @bucket_name,
165
+ key: @object_key,
166
+ upload_id: @id
167
+ )
168
+ resp = @client.complete_multipart_upload(options)
169
+ Object.new(
170
+ bucket_name: @bucket_name,
171
+ key: @object_key,
172
+ client: @client
173
+ )
174
+ end
175
+
176
+ # @!group Associations
177
+
178
+ # @return [Object]
179
+ def object
180
+ Object.new(
181
+ bucket_name: @bucket_name,
182
+ key: @object_key,
183
+ client: @client
184
+ )
185
+ end
186
+
187
+ # @param [String] part_number
188
+ # @return [MultipartUploadPart]
189
+ def part(part_number)
190
+ MultipartUploadPart.new(
191
+ bucket_name: @bucket_name,
192
+ object_key: @object_key,
193
+ multipart_upload_id: @id,
194
+ part_number: part_number,
195
+ client: @client
196
+ )
197
+ end
198
+
199
+ # @example Request syntax with placeholder values
200
+ #
201
+ # parts = multipart_upload.parts({
202
+ # request_payer: "requester", # accepts requester
203
+ # })
204
+ # @param [Hash] options ({})
205
+ # @option options [String] :request_payer
206
+ # Confirms that the requester knows that she or he will be charged for
207
+ # the request. Bucket owners need not specify this parameter in their
208
+ # requests. Documentation on downloading objects from requester pays
209
+ # buckets can be found at
210
+ # http://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectsinRequesterPaysBuckets.html
211
+ # @return [MultipartUploadPart::Collection]
212
+ def parts(options = {})
213
+ batches = Enumerator.new do |y|
214
+ options = options.merge(
215
+ bucket: @bucket_name,
216
+ key: @object_key,
217
+ upload_id: @id
218
+ )
219
+ resp = @client.list_parts(options)
220
+ resp.each_page do |page|
221
+ batch = []
222
+ page.data.parts.each do |p|
223
+ batch << MultipartUploadPart.new(
224
+ bucket_name: options[:bucket],
225
+ object_key: options[:key],
226
+ multipart_upload_id: options[:upload_id],
227
+ part_number: p.part_number,
228
+ data: p,
229
+ client: @client
230
+ )
231
+ end
232
+ y.yield(batch)
233
+ end
234
+ end
235
+ MultipartUploadPart::Collection.new(batches)
236
+ end
237
+
238
+ # @deprecated
239
+ # @api private
240
+ def identifiers
241
+ {
242
+ bucket_name: @bucket_name,
243
+ object_key: @object_key,
244
+ id: @id
245
+ }
246
+ end
247
+ deprecated(:identifiers)
248
+
249
+ private
250
+
251
+ def extract_bucket_name(args, options)
252
+ value = args[0] || options.delete(:bucket_name)
253
+ case value
254
+ when String then value
255
+ when nil then raise ArgumentError, "missing required option :bucket_name"
256
+ else
257
+ msg = "expected :bucket_name to be a String, got #{value.class}"
258
+ raise ArgumentError, msg
259
+ end
260
+ end
261
+
262
+ def extract_object_key(args, options)
263
+ value = args[1] || options.delete(:object_key)
264
+ case value
265
+ when String then value
266
+ when nil then raise ArgumentError, "missing required option :object_key"
267
+ else
268
+ msg = "expected :object_key to be a String, got #{value.class}"
269
+ raise ArgumentError, msg
270
+ end
271
+ end
272
+
273
+ def extract_id(args, options)
274
+ value = args[2] || options.delete(:id)
275
+ case value
276
+ when String then value
277
+ when nil then raise ArgumentError, "missing required option :id"
278
+ else
279
+ msg = "expected :id to be a String, got #{value.class}"
280
+ raise ArgumentError, msg
281
+ end
282
+ end
283
+
284
+ class Collection < Aws::Resources::Collection; end
285
+ end
286
+ end
287
+ end