aws-sdk-s3 1.75.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 (91) hide show
  1. checksums.yaml +7 -0
  2. data/lib/aws-sdk-s3.rb +73 -0
  3. data/lib/aws-sdk-s3/bucket.rb +861 -0
  4. data/lib/aws-sdk-s3/bucket_acl.rb +277 -0
  5. data/lib/aws-sdk-s3/bucket_cors.rb +262 -0
  6. data/lib/aws-sdk-s3/bucket_lifecycle.rb +264 -0
  7. data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +283 -0
  8. data/lib/aws-sdk-s3/bucket_logging.rb +251 -0
  9. data/lib/aws-sdk-s3/bucket_notification.rb +293 -0
  10. data/lib/aws-sdk-s3/bucket_policy.rb +242 -0
  11. data/lib/aws-sdk-s3/bucket_region_cache.rb +81 -0
  12. data/lib/aws-sdk-s3/bucket_request_payment.rb +236 -0
  13. data/lib/aws-sdk-s3/bucket_tagging.rb +251 -0
  14. data/lib/aws-sdk-s3/bucket_versioning.rb +312 -0
  15. data/lib/aws-sdk-s3/bucket_website.rb +292 -0
  16. data/lib/aws-sdk-s3/client.rb +11818 -0
  17. data/lib/aws-sdk-s3/client_api.rb +3014 -0
  18. data/lib/aws-sdk-s3/customizations.rb +34 -0
  19. data/lib/aws-sdk-s3/customizations/bucket.rb +162 -0
  20. data/lib/aws-sdk-s3/customizations/multipart_upload.rb +44 -0
  21. data/lib/aws-sdk-s3/customizations/object.rb +389 -0
  22. data/lib/aws-sdk-s3/customizations/object_summary.rb +85 -0
  23. data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +13 -0
  24. data/lib/aws-sdk-s3/encryption.rb +21 -0
  25. data/lib/aws-sdk-s3/encryption/client.rb +375 -0
  26. data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +190 -0
  27. data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +65 -0
  28. data/lib/aws-sdk-s3/encryption/default_key_provider.rb +40 -0
  29. data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +61 -0
  30. data/lib/aws-sdk-s3/encryption/errors.rb +15 -0
  31. data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +58 -0
  32. data/lib/aws-sdk-s3/encryption/io_decrypter.rb +36 -0
  33. data/lib/aws-sdk-s3/encryption/io_encrypter.rb +71 -0
  34. data/lib/aws-sdk-s3/encryption/key_provider.rb +31 -0
  35. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +75 -0
  36. data/lib/aws-sdk-s3/encryption/materials.rb +60 -0
  37. data/lib/aws-sdk-s3/encryption/utils.rb +81 -0
  38. data/lib/aws-sdk-s3/encryptionV2/client.rb +388 -0
  39. data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +198 -0
  40. data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +103 -0
  41. data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +38 -0
  42. data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +66 -0
  43. data/lib/aws-sdk-s3/encryptionV2/errors.rb +13 -0
  44. data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +56 -0
  45. data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +35 -0
  46. data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +71 -0
  47. data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +29 -0
  48. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +99 -0
  49. data/lib/aws-sdk-s3/encryptionV2/materials.rb +58 -0
  50. data/lib/aws-sdk-s3/encryptionV2/utils.rb +116 -0
  51. data/lib/aws-sdk-s3/encryption_v2.rb +20 -0
  52. data/lib/aws-sdk-s3/errors.rb +115 -0
  53. data/lib/aws-sdk-s3/event_streams.rb +69 -0
  54. data/lib/aws-sdk-s3/file_downloader.rb +142 -0
  55. data/lib/aws-sdk-s3/file_part.rb +78 -0
  56. data/lib/aws-sdk-s3/file_uploader.rb +70 -0
  57. data/lib/aws-sdk-s3/legacy_signer.rb +189 -0
  58. data/lib/aws-sdk-s3/multipart_file_uploader.rb +227 -0
  59. data/lib/aws-sdk-s3/multipart_stream_uploader.rb +173 -0
  60. data/lib/aws-sdk-s3/multipart_upload.rb +401 -0
  61. data/lib/aws-sdk-s3/multipart_upload_error.rb +18 -0
  62. data/lib/aws-sdk-s3/multipart_upload_part.rb +423 -0
  63. data/lib/aws-sdk-s3/object.rb +1422 -0
  64. data/lib/aws-sdk-s3/object_acl.rb +333 -0
  65. data/lib/aws-sdk-s3/object_copier.rb +101 -0
  66. data/lib/aws-sdk-s3/object_multipart_copier.rb +182 -0
  67. data/lib/aws-sdk-s3/object_summary.rb +1181 -0
  68. data/lib/aws-sdk-s3/object_version.rb +550 -0
  69. data/lib/aws-sdk-s3/plugins/accelerate.rb +87 -0
  70. data/lib/aws-sdk-s3/plugins/bucket_arn.rb +212 -0
  71. data/lib/aws-sdk-s3/plugins/bucket_dns.rb +91 -0
  72. data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +45 -0
  73. data/lib/aws-sdk-s3/plugins/dualstack.rb +74 -0
  74. data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +28 -0
  75. data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +25 -0
  76. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +55 -0
  77. data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +62 -0
  78. data/lib/aws-sdk-s3/plugins/location_constraint.rb +35 -0
  79. data/lib/aws-sdk-s3/plugins/md5s.rb +84 -0
  80. data/lib/aws-sdk-s3/plugins/redirects.rb +45 -0
  81. data/lib/aws-sdk-s3/plugins/s3_host_id.rb +30 -0
  82. data/lib/aws-sdk-s3/plugins/s3_signer.rb +222 -0
  83. data/lib/aws-sdk-s3/plugins/sse_cpk.rb +70 -0
  84. data/lib/aws-sdk-s3/plugins/streaming_retry.rb +118 -0
  85. data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +97 -0
  86. data/lib/aws-sdk-s3/presigned_post.rb +686 -0
  87. data/lib/aws-sdk-s3/presigner.rb +253 -0
  88. data/lib/aws-sdk-s3/resource.rb +117 -0
  89. data/lib/aws-sdk-s3/types.rb +13154 -0
  90. data/lib/aws-sdk-s3/waiters.rb +243 -0
  91. metadata +184 -0
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'set'
5
+
6
+ module Aws
7
+ module S3
8
+ # @api private
9
+ class MultipartFileUploader
10
+
11
+ MIN_PART_SIZE = 5 * 1024 * 1024 # 5MB
12
+
13
+ FILE_TOO_SMALL = "unable to multipart upload files smaller than 5MB"
14
+
15
+ MAX_PARTS = 10_000
16
+
17
+ THREAD_COUNT = 10
18
+
19
+ # @api private
20
+ CREATE_OPTIONS = Set.new(
21
+ Client.api.operation(:create_multipart_upload).input.shape.member_names
22
+ )
23
+
24
+ # @api private
25
+ UPLOAD_PART_OPTIONS = Set.new(
26
+ Client.api.operation(:upload_part).input.shape.member_names
27
+ )
28
+
29
+ # @option options [Client] :client
30
+ # @option options [Integer] :thread_count (THREAD_COUNT)
31
+ def initialize(options = {})
32
+ @client = options[:client] || Client.new
33
+ @thread_count = options[:thread_count] || THREAD_COUNT
34
+ end
35
+
36
+ # @return [Client]
37
+ attr_reader :client
38
+
39
+ # @param [String, Pathname, File, Tempfile] source The file to upload.
40
+ # @option options [required, String] :bucket The bucket to upload to.
41
+ # @option options [required, String] :key The key for the object.
42
+ # @option options [Proc] :progress_callback
43
+ # A Proc that will be called when each chunk of the upload is sent.
44
+ # It will be invoked with [bytes_read], [total_sizes]
45
+ # @return [void]
46
+ def upload(source, options = {})
47
+ if File.size(source) < MIN_PART_SIZE
48
+ raise ArgumentError, FILE_TOO_SMALL
49
+ else
50
+ upload_id = initiate_upload(options)
51
+ parts = upload_parts(upload_id, source, options)
52
+ complete_upload(upload_id, parts, options)
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def initiate_upload(options)
59
+ @client.create_multipart_upload(create_opts(options)).upload_id
60
+ end
61
+
62
+ def complete_upload(upload_id, parts, options)
63
+ @client.complete_multipart_upload(
64
+ bucket: options[:bucket],
65
+ key: options[:key],
66
+ upload_id: upload_id,
67
+ multipart_upload: { parts: parts }
68
+ )
69
+ end
70
+
71
+ def upload_parts(upload_id, source, options)
72
+ pending = PartList.new(compute_parts(upload_id, source, options))
73
+ completed = PartList.new
74
+ errors = upload_in_threads(pending, completed, options)
75
+ if errors.empty?
76
+ completed.to_a.sort_by { |part| part[:part_number] }
77
+ else
78
+ abort_upload(upload_id, options, errors)
79
+ end
80
+ end
81
+
82
+ def abort_upload(upload_id, options, errors)
83
+ @client.abort_multipart_upload(
84
+ bucket: options[:bucket],
85
+ key: options[:key],
86
+ upload_id: upload_id
87
+ )
88
+ msg = "multipart upload failed: #{errors.map(&:message).join("; ")}"
89
+ raise MultipartUploadError.new(msg, errors)
90
+ rescue MultipartUploadError => error
91
+ raise error
92
+ rescue => error
93
+ msg = "failed to abort multipart upload: #{error.message}"
94
+ raise MultipartUploadError.new(msg, errors + [error])
95
+ end
96
+
97
+ def compute_parts(upload_id, source, options)
98
+ size = File.size(source)
99
+ default_part_size = compute_default_part_size(size)
100
+ offset = 0
101
+ part_number = 1
102
+ parts = []
103
+ while offset < size
104
+ parts << upload_part_opts(options).merge(
105
+ upload_id: upload_id,
106
+ part_number: part_number,
107
+ body: FilePart.new(
108
+ source: source,
109
+ offset: offset,
110
+ size: part_size(size, default_part_size, offset)
111
+ )
112
+ )
113
+ part_number += 1
114
+ offset += default_part_size
115
+ end
116
+ parts
117
+ end
118
+
119
+ def create_opts(options)
120
+ CREATE_OPTIONS.inject({}) do |hash, key|
121
+ hash[key] = options[key] if options.key?(key)
122
+ hash
123
+ end
124
+ end
125
+
126
+ def upload_part_opts(options)
127
+ UPLOAD_PART_OPTIONS.inject({}) do |hash, key|
128
+ hash[key] = options[key] if options.key?(key)
129
+ hash
130
+ end
131
+ end
132
+
133
+ def upload_in_threads(pending, completed, options)
134
+ threads = []
135
+ if (callback = options[:progress_callback])
136
+ progress = MultipartProgress.new(pending, callback)
137
+ end
138
+ @thread_count.times do
139
+ thread = Thread.new do
140
+ begin
141
+ while part = pending.shift
142
+ if progress
143
+ part[:on_chunk_sent] =
144
+ proc do |_chunk, bytes, _total|
145
+ progress.call(part[:part_number], bytes)
146
+ end
147
+ end
148
+ resp = @client.upload_part(part)
149
+ part[:body].close
150
+ completed.push(etag: resp.etag, part_number: part[:part_number])
151
+ end
152
+ nil
153
+ rescue => error
154
+ # keep other threads from uploading other parts
155
+ pending.clear!
156
+ error
157
+ end
158
+ end
159
+ thread.abort_on_exception = true
160
+ threads << thread
161
+ end
162
+ threads.map(&:value).compact
163
+ end
164
+
165
+ def compute_default_part_size(source_size)
166
+ [(source_size.to_f / MAX_PARTS).ceil, MIN_PART_SIZE].max.to_i
167
+ end
168
+
169
+ def part_size(total_size, part_size, offset)
170
+ if offset + part_size > total_size
171
+ total_size - offset
172
+ else
173
+ part_size
174
+ end
175
+ end
176
+
177
+ # @api private
178
+ class PartList
179
+
180
+ def initialize(parts = [])
181
+ @parts = parts
182
+ @mutex = Mutex.new
183
+ end
184
+
185
+ def push(part)
186
+ @mutex.synchronize { @parts.push(part) }
187
+ end
188
+
189
+ def shift
190
+ @mutex.synchronize { @parts.shift }
191
+ end
192
+
193
+ def clear!
194
+ @mutex.synchronize { @parts.clear }
195
+ end
196
+
197
+ def size
198
+ @mutex.synchronize { @parts.size }
199
+ end
200
+
201
+ def part_sizes
202
+ @mutex.synchronize { @parts.map { |p| p[:body].size } }
203
+ end
204
+
205
+ def to_a
206
+ @mutex.synchronize { @parts.dup }
207
+ end
208
+
209
+ end
210
+
211
+ # @api private
212
+ class MultipartProgress
213
+ def initialize(parts, progress_callback)
214
+ @bytes_sent = Array.new(parts.size, 0)
215
+ @total_sizes = parts.part_sizes
216
+ @progress_callback = progress_callback
217
+ end
218
+
219
+ def call(part_number, bytes_read)
220
+ # part numbers start at 1
221
+ @bytes_sent[part_number - 1] = bytes_read
222
+ @progress_callback.call(@bytes_sent, @total_sizes)
223
+ end
224
+ end
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thread'
4
+ require 'set'
5
+ require 'tempfile'
6
+ require 'stringio'
7
+
8
+ module Aws
9
+ module S3
10
+ # @api private
11
+ class MultipartStreamUploader
12
+ # api private
13
+ PART_SIZE = 5 * 1024 * 1024 # 5MB
14
+
15
+ # api private
16
+ THREAD_COUNT = 10
17
+
18
+ # api private
19
+ TEMPFILE_PREIX = 'aws-sdk-s3-upload_stream'.freeze
20
+
21
+ # @api private
22
+ CREATE_OPTIONS =
23
+ Set.new(Client.api.operation(:create_multipart_upload).input.shape.member_names)
24
+
25
+ # @api private
26
+ UPLOAD_PART_OPTIONS =
27
+ Set.new(Client.api.operation(:upload_part).input.shape.member_names)
28
+
29
+ # @option options [Client] :client
30
+ def initialize(options = {})
31
+ @client = options[:client] || Client.new
32
+ @tempfile = options[:tempfile]
33
+ @part_size = options[:part_size] || PART_SIZE
34
+ @thread_count = options[:thread_count] || THREAD_COUNT
35
+ end
36
+
37
+ # @return [Client]
38
+ attr_reader :client
39
+
40
+ # @option options [required,String] :bucket
41
+ # @option options [required,String] :key
42
+ # @return [void]
43
+ def upload(options = {}, &block)
44
+ upload_id = initiate_upload(options)
45
+ parts = upload_parts(upload_id, options, &block)
46
+ complete_upload(upload_id, parts, options)
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, options, &block)
64
+ completed = Queue.new
65
+ errors = begin
66
+ IO.pipe do |read_pipe, write_pipe|
67
+ threads = upload_in_threads(read_pipe, completed, upload_part_opts(options).merge(upload_id: upload_id))
68
+ begin
69
+ block.call(write_pipe)
70
+ ensure
71
+ # Ensure the pipe is closed to avoid https://github.com/jruby/jruby/issues/6111
72
+ write_pipe.close
73
+ end
74
+ threads.map(&:value).compact
75
+ end
76
+ rescue => e
77
+ [e]
78
+ end
79
+
80
+ if errors.empty?
81
+ Array.new(completed.size) { completed.pop }.sort_by { |part| part[:part_number] }
82
+ else
83
+ abort_upload(upload_id, options, errors)
84
+ end
85
+ end
86
+
87
+ def abort_upload(upload_id, options, errors)
88
+ @client.abort_multipart_upload(
89
+ bucket: options[:bucket],
90
+ key: options[:key],
91
+ upload_id: upload_id
92
+ )
93
+ msg = "multipart upload failed: #{errors.map(&:message).join("; ")}"
94
+ raise MultipartUploadError.new(msg, errors)
95
+ rescue MultipartUploadError => error
96
+ raise error
97
+ rescue => error
98
+ msg = "failed to abort multipart upload: #{error.message}"
99
+ raise MultipartUploadError.new(msg, errors + [error])
100
+ end
101
+
102
+ def create_opts(options)
103
+ CREATE_OPTIONS.inject({}) do |hash, key|
104
+ hash[key] = options[key] if options.key?(key)
105
+ hash
106
+ end
107
+ end
108
+
109
+ def upload_part_opts(options)
110
+ UPLOAD_PART_OPTIONS.inject({}) do |hash, key|
111
+ hash[key] = options[key] if options.key?(key)
112
+ hash
113
+ end
114
+ end
115
+
116
+ def read_to_part_body(read_pipe)
117
+ return if read_pipe.closed?
118
+ temp_io = @tempfile ? Tempfile.new(TEMPFILE_PREIX) : StringIO.new(String.new)
119
+ temp_io.binmode
120
+ bytes_copied = IO.copy_stream(read_pipe, temp_io, @part_size)
121
+ temp_io.rewind
122
+ if bytes_copied == 0
123
+ if Tempfile === temp_io
124
+ temp_io.close
125
+ temp_io.unlink
126
+ end
127
+ nil
128
+ else
129
+ temp_io
130
+ end
131
+ end
132
+
133
+ def upload_in_threads(read_pipe, completed, options)
134
+ mutex = Mutex.new
135
+ part_number = 0
136
+ @thread_count.times.map do
137
+ thread = Thread.new do
138
+ begin
139
+ loop do
140
+ body, thread_part_number = mutex.synchronize do
141
+ [read_to_part_body(read_pipe), part_number += 1]
142
+ end
143
+ break unless (body || thread_part_number == 1)
144
+ begin
145
+ part = options.merge(
146
+ body: body,
147
+ part_number: thread_part_number,
148
+ )
149
+ resp = @client.upload_part(part)
150
+ completed << {etag: resp.etag, part_number: part[:part_number]}
151
+ ensure
152
+ if Tempfile === body
153
+ body.close
154
+ body.unlink
155
+ elsif StringIO === body
156
+ body.string.clear
157
+ end
158
+ end
159
+ end
160
+ nil
161
+ rescue => error
162
+ # keep other threads from uploading other parts
163
+ mutex.synchronize { read_pipe.close_read unless read_pipe.closed? }
164
+ error
165
+ end
166
+ end
167
+ thread.abort_on_exception = true
168
+ thread
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,401 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WARNING ABOUT GENERATED CODE
4
+ #
5
+ # This file is generated. See the contributing guide for more information:
6
+ # https://github.com/aws/aws-sdk-ruby/blob/master/CONTRIBUTING.md
7
+ #
8
+ # WARNING ABOUT GENERATED CODE
9
+
10
+ module Aws::S3
11
+
12
+ class MultipartUpload
13
+
14
+ extend Aws::Deprecations
15
+
16
+ # @overload def initialize(bucket_name, object_key, id, options = {})
17
+ # @param [String] bucket_name
18
+ # @param [String] object_key
19
+ # @param [String] id
20
+ # @option options [Client] :client
21
+ # @overload def initialize(options = {})
22
+ # @option options [required, String] :bucket_name
23
+ # @option options [required, String] :object_key
24
+ # @option options [required, String] :id
25
+ # @option options [Client] :client
26
+ def initialize(*args)
27
+ options = Hash === args.last ? args.pop.dup : {}
28
+ @bucket_name = extract_bucket_name(args, options)
29
+ @object_key = extract_object_key(args, options)
30
+ @id = extract_id(args, options)
31
+ @data = options.delete(:data)
32
+ @client = options.delete(:client) || Client.new(options)
33
+ @waiter_block_warned = false
34
+ end
35
+
36
+ # @!group Read-Only Attributes
37
+
38
+ # @return [String]
39
+ def bucket_name
40
+ @bucket_name
41
+ end
42
+
43
+ # @return [String]
44
+ def object_key
45
+ @object_key
46
+ end
47
+
48
+ # @return [String]
49
+ def id
50
+ @id
51
+ end
52
+
53
+ # Upload ID that identifies the multipart upload.
54
+ # @return [String]
55
+ def upload_id
56
+ data[:upload_id]
57
+ end
58
+
59
+ # Key of the object for which the multipart upload was initiated.
60
+ # @return [String]
61
+ def key
62
+ data[:key]
63
+ end
64
+
65
+ # Date and time at which the multipart upload was initiated.
66
+ # @return [Time]
67
+ def initiated
68
+ data[:initiated]
69
+ end
70
+
71
+ # The class of storage used to store the object.
72
+ # @return [String]
73
+ def storage_class
74
+ data[:storage_class]
75
+ end
76
+
77
+ # Specifies the owner of the object that is part of the multipart
78
+ # upload.
79
+ # @return [Types::Owner]
80
+ def owner
81
+ data[:owner]
82
+ end
83
+
84
+ # Identifies who initiated the multipart upload.
85
+ # @return [Types::Initiator]
86
+ def initiator
87
+ data[:initiator]
88
+ end
89
+
90
+ # @!endgroup
91
+
92
+ # @return [Client]
93
+ def client
94
+ @client
95
+ end
96
+
97
+ # @raise [NotImplementedError]
98
+ # @api private
99
+ def load
100
+ msg = "#load is not implemented, data only available via enumeration"
101
+ raise NotImplementedError, msg
102
+ end
103
+ alias :reload :load
104
+
105
+ # @raise [NotImplementedError] Raises when {#data_loaded?} is `false`.
106
+ # @return [Types::MultipartUpload]
107
+ # Returns the data for this {MultipartUpload}.
108
+ def data
109
+ load unless @data
110
+ @data
111
+ end
112
+
113
+ # @return [Boolean]
114
+ # Returns `true` if this resource is loaded. Accessing attributes or
115
+ # {#data} on an unloaded resource will trigger a call to {#load}.
116
+ def data_loaded?
117
+ !!@data
118
+ end
119
+
120
+ # @deprecated Use [Aws::S3::Client] #wait_until instead
121
+ #
122
+ # Waiter polls an API operation until a resource enters a desired
123
+ # state.
124
+ #
125
+ # @note The waiting operation is performed on a copy. The original resource
126
+ # remains unchanged.
127
+ #
128
+ # ## Basic Usage
129
+ #
130
+ # Waiter will polls until it is successful, it fails by
131
+ # entering a terminal state, or until a maximum number of attempts
132
+ # are made.
133
+ #
134
+ # # polls in a loop until condition is true
135
+ # resource.wait_until(options) {|resource| condition}
136
+ #
137
+ # ## Example
138
+ #
139
+ # instance.wait_until(max_attempts:10, delay:5) do |instance|
140
+ # instance.state.name == 'running'
141
+ # end
142
+ #
143
+ # ## Configuration
144
+ #
145
+ # You can configure the maximum number of polling attempts, and the
146
+ # delay (in seconds) between each polling attempt. The waiting condition is
147
+ # set by passing a block to {#wait_until}:
148
+ #
149
+ # # poll for ~25 seconds
150
+ # resource.wait_until(max_attempts:5,delay:5) {|resource|...}
151
+ #
152
+ # ## Callbacks
153
+ #
154
+ # You can be notified before each polling attempt and before each
155
+ # delay. If you throw `:success` or `:failure` from these callbacks,
156
+ # it will terminate the waiter.
157
+ #
158
+ # started_at = Time.now
159
+ # # poll for 1 hour, instead of a number of attempts
160
+ # proc = Proc.new do |attempts, response|
161
+ # throw :failure if Time.now - started_at > 3600
162
+ # end
163
+ #
164
+ # # disable max attempts
165
+ # instance.wait_until(before_wait:proc, max_attempts:nil) {...}
166
+ #
167
+ # ## Handling Errors
168
+ #
169
+ # When a waiter is successful, it returns the Resource. When a waiter
170
+ # fails, it raises an error.
171
+ #
172
+ # begin
173
+ # resource.wait_until(...)
174
+ # rescue Aws::Waiters::Errors::WaiterFailed
175
+ # # resource did not enter the desired state in time
176
+ # end
177
+ #
178
+ # @yieldparam [Resource] resource to be used in the waiting condition.
179
+ #
180
+ # @raise [Aws::Waiters::Errors::FailureStateError] Raised when the waiter
181
+ # terminates because the waiter has entered a state that it will not
182
+ # transition out of, preventing success.
183
+ #
184
+ # yet successful.
185
+ #
186
+ # @raise [Aws::Waiters::Errors::UnexpectedError] Raised when an error is
187
+ # encountered while polling for a resource that is not expected.
188
+ #
189
+ # @raise [NotImplementedError] Raised when the resource does not
190
+ #
191
+ # @option options [Integer] :max_attempts (10) Maximum number of
192
+ # attempts
193
+ # @option options [Integer] :delay (10) Delay between each
194
+ # attempt in seconds
195
+ # @option options [Proc] :before_attempt (nil) Callback
196
+ # invoked before each attempt
197
+ # @option options [Proc] :before_wait (nil) Callback
198
+ # invoked before each wait
199
+ # @return [Resource] if the waiter was successful
200
+ def wait_until(options = {}, &block)
201
+ self_copy = self.dup
202
+ attempts = 0
203
+ options[:max_attempts] = 10 unless options.key?(:max_attempts)
204
+ options[:delay] ||= 10
205
+ options[:poller] = Proc.new do
206
+ attempts += 1
207
+ if block.call(self_copy)
208
+ [:success, self_copy]
209
+ else
210
+ self_copy.reload unless attempts == options[:max_attempts]
211
+ :retry
212
+ end
213
+ end
214
+ Aws::Waiters::Waiter.new(options).wait({})
215
+ end
216
+
217
+ # @!group Actions
218
+
219
+ # @example Request syntax with placeholder values
220
+ #
221
+ # multipart_upload.abort({
222
+ # request_payer: "requester", # accepts requester
223
+ # })
224
+ # @param [Hash] options ({})
225
+ # @option options [String] :request_payer
226
+ # Confirms that the requester knows that they will be charged for the
227
+ # request. Bucket owners need not specify this parameter in their
228
+ # requests. For information about downloading objects from requester
229
+ # pays buckets, see [Downloading Objects in Requestor Pays Buckets][1]
230
+ # in the *Amazon S3 Developer Guide*.
231
+ #
232
+ #
233
+ #
234
+ # [1]: https://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectsinRequesterPaysBuckets.html
235
+ # @return [Types::AbortMultipartUploadOutput]
236
+ def abort(options = {})
237
+ options = options.merge(
238
+ bucket: @bucket_name,
239
+ key: @object_key,
240
+ upload_id: @id
241
+ )
242
+ resp = @client.abort_multipart_upload(options)
243
+ resp.data
244
+ end
245
+
246
+ # @example Request syntax with placeholder values
247
+ #
248
+ # object = multipart_upload.complete({
249
+ # multipart_upload: {
250
+ # parts: [
251
+ # {
252
+ # etag: "ETag",
253
+ # part_number: 1,
254
+ # },
255
+ # ],
256
+ # },
257
+ # request_payer: "requester", # accepts requester
258
+ # })
259
+ # @param [Hash] options ({})
260
+ # @option options [Types::CompletedMultipartUpload] :multipart_upload
261
+ # The container for the multipart upload request information.
262
+ # @option options [String] :request_payer
263
+ # Confirms that the requester knows that they will be charged for the
264
+ # request. Bucket owners need not specify this parameter in their
265
+ # requests. For information about downloading objects from requester
266
+ # pays buckets, see [Downloading Objects in Requestor Pays Buckets][1]
267
+ # in the *Amazon S3 Developer Guide*.
268
+ #
269
+ #
270
+ #
271
+ # [1]: https://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectsinRequesterPaysBuckets.html
272
+ # @return [Object]
273
+ def complete(options = {})
274
+ options = options.merge(
275
+ bucket: @bucket_name,
276
+ key: @object_key,
277
+ upload_id: @id
278
+ )
279
+ @client.complete_multipart_upload(options)
280
+ Object.new(
281
+ bucket_name: @bucket_name,
282
+ key: @object_key,
283
+ client: @client
284
+ )
285
+ end
286
+
287
+ # @!group Associations
288
+
289
+ # @return [Object]
290
+ def object
291
+ Object.new(
292
+ bucket_name: @bucket_name,
293
+ key: @object_key,
294
+ client: @client
295
+ )
296
+ end
297
+
298
+ # @param [String] part_number
299
+ # @return [MultipartUploadPart]
300
+ def part(part_number)
301
+ MultipartUploadPart.new(
302
+ bucket_name: @bucket_name,
303
+ object_key: @object_key,
304
+ multipart_upload_id: @id,
305
+ part_number: part_number,
306
+ client: @client
307
+ )
308
+ end
309
+
310
+ # @example Request syntax with placeholder values
311
+ #
312
+ # parts = multipart_upload.parts({
313
+ # request_payer: "requester", # accepts requester
314
+ # })
315
+ # @param [Hash] options ({})
316
+ # @option options [String] :request_payer
317
+ # Confirms that the requester knows that they will be charged for the
318
+ # request. Bucket owners need not specify this parameter in their
319
+ # requests. For information about downloading objects from requester
320
+ # pays buckets, see [Downloading Objects in Requestor Pays Buckets][1]
321
+ # in the *Amazon S3 Developer Guide*.
322
+ #
323
+ #
324
+ #
325
+ # [1]: https://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectsinRequesterPaysBuckets.html
326
+ # @return [MultipartUploadPart::Collection]
327
+ def parts(options = {})
328
+ batches = Enumerator.new do |y|
329
+ options = options.merge(
330
+ bucket: @bucket_name,
331
+ key: @object_key,
332
+ upload_id: @id
333
+ )
334
+ resp = @client.list_parts(options)
335
+ resp.each_page do |page|
336
+ batch = []
337
+ page.data.parts.each do |p|
338
+ batch << MultipartUploadPart.new(
339
+ bucket_name: options[:bucket],
340
+ object_key: options[:key],
341
+ multipart_upload_id: options[:upload_id],
342
+ part_number: p.part_number,
343
+ data: p,
344
+ client: @client
345
+ )
346
+ end
347
+ y.yield(batch)
348
+ end
349
+ end
350
+ MultipartUploadPart::Collection.new(batches)
351
+ end
352
+
353
+ # @deprecated
354
+ # @api private
355
+ def identifiers
356
+ {
357
+ bucket_name: @bucket_name,
358
+ object_key: @object_key,
359
+ id: @id
360
+ }
361
+ end
362
+ deprecated(:identifiers)
363
+
364
+ private
365
+
366
+ def extract_bucket_name(args, options)
367
+ value = args[0] || options.delete(:bucket_name)
368
+ case value
369
+ when String then value
370
+ when nil then raise ArgumentError, "missing required option :bucket_name"
371
+ else
372
+ msg = "expected :bucket_name to be a String, got #{value.class}"
373
+ raise ArgumentError, msg
374
+ end
375
+ end
376
+
377
+ def extract_object_key(args, options)
378
+ value = args[1] || options.delete(:object_key)
379
+ case value
380
+ when String then value
381
+ when nil then raise ArgumentError, "missing required option :object_key"
382
+ else
383
+ msg = "expected :object_key to be a String, got #{value.class}"
384
+ raise ArgumentError, msg
385
+ end
386
+ end
387
+
388
+ def extract_id(args, options)
389
+ value = args[2] || options.delete(:id)
390
+ case value
391
+ when String then value
392
+ when nil then raise ArgumentError, "missing required option :id"
393
+ else
394
+ msg = "expected :id to be a String, got #{value.class}"
395
+ raise ArgumentError, msg
396
+ end
397
+ end
398
+
399
+ class Collection < Aws::Resources::Collection; end
400
+ end
401
+ end