aws-sdk-s3 1.122.0 → 1.157.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +238 -0
  3. data/VERSION +1 -1
  4. data/lib/aws-sdk-s3/access_grants_credentials.rb +57 -0
  5. data/lib/aws-sdk-s3/access_grants_credentials_provider.rb +250 -0
  6. data/lib/aws-sdk-s3/bucket.rb +507 -114
  7. data/lib/aws-sdk-s3/bucket_acl.rb +18 -12
  8. data/lib/aws-sdk-s3/bucket_cors.rb +24 -16
  9. data/lib/aws-sdk-s3/bucket_lifecycle.rb +24 -16
  10. data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +24 -16
  11. data/lib/aws-sdk-s3/bucket_logging.rb +25 -12
  12. data/lib/aws-sdk-s3/bucket_notification.rb +12 -6
  13. data/lib/aws-sdk-s3/bucket_policy.rb +70 -18
  14. data/lib/aws-sdk-s3/bucket_region_cache.rb +9 -5
  15. data/lib/aws-sdk-s3/bucket_request_payment.rb +18 -12
  16. data/lib/aws-sdk-s3/bucket_tagging.rb +24 -16
  17. data/lib/aws-sdk-s3/bucket_versioning.rb +42 -32
  18. data/lib/aws-sdk-s3/bucket_website.rb +24 -16
  19. data/lib/aws-sdk-s3/client.rb +6048 -2781
  20. data/lib/aws-sdk-s3/client_api.rb +148 -20
  21. data/lib/aws-sdk-s3/customizations/bucket.rb +3 -1
  22. data/lib/aws-sdk-s3/customizations/errors.rb +16 -3
  23. data/lib/aws-sdk-s3/customizations/object.rb +91 -18
  24. data/lib/aws-sdk-s3/customizations.rb +8 -0
  25. data/lib/aws-sdk-s3/encryption/client.rb +6 -2
  26. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +13 -9
  27. data/lib/aws-sdk-s3/encryptionV2/client.rb +6 -2
  28. data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +1 -0
  29. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +10 -6
  30. data/lib/aws-sdk-s3/endpoint_parameters.rb +44 -0
  31. data/lib/aws-sdk-s3/endpoint_provider.rb +111 -252
  32. data/lib/aws-sdk-s3/endpoints.rb +540 -0
  33. data/lib/aws-sdk-s3/express_credentials.rb +55 -0
  34. data/lib/aws-sdk-s3/express_credentials_provider.rb +59 -0
  35. data/lib/aws-sdk-s3/file_downloader.rb +169 -44
  36. data/lib/aws-sdk-s3/file_uploader.rb +8 -6
  37. data/lib/aws-sdk-s3/multipart_file_uploader.rb +4 -4
  38. data/lib/aws-sdk-s3/multipart_stream_uploader.rb +10 -7
  39. data/lib/aws-sdk-s3/multipart_upload.rb +81 -20
  40. data/lib/aws-sdk-s3/multipart_upload_part.rb +169 -38
  41. data/lib/aws-sdk-s3/object.rb +1571 -261
  42. data/lib/aws-sdk-s3/object_acl.rb +38 -18
  43. data/lib/aws-sdk-s3/object_copier.rb +7 -5
  44. data/lib/aws-sdk-s3/object_multipart_copier.rb +42 -24
  45. data/lib/aws-sdk-s3/object_summary.rb +1422 -276
  46. data/lib/aws-sdk-s3/object_version.rb +344 -47
  47. data/lib/aws-sdk-s3/plugins/access_grants.rb +178 -0
  48. data/lib/aws-sdk-s3/plugins/endpoints.rb +14 -2
  49. data/lib/aws-sdk-s3/plugins/express_session_auth.rb +97 -0
  50. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +53 -16
  51. data/lib/aws-sdk-s3/plugins/location_constraint.rb +3 -1
  52. data/lib/aws-sdk-s3/plugins/md5s.rb +2 -1
  53. data/lib/aws-sdk-s3/plugins/s3_signer.rb +7 -2
  54. data/lib/aws-sdk-s3/presigned_post.rb +52 -43
  55. data/lib/aws-sdk-s3/presigner.rb +8 -4
  56. data/lib/aws-sdk-s3/resource.rb +89 -13
  57. data/lib/aws-sdk-s3/types.rb +4879 -1513
  58. data/lib/aws-sdk-s3.rb +1 -1
  59. data/sig/bucket.rbs +212 -0
  60. data/sig/bucket_acl.rbs +78 -0
  61. data/sig/bucket_cors.rbs +69 -0
  62. data/sig/bucket_lifecycle.rbs +88 -0
  63. data/sig/bucket_lifecycle_configuration.rbs +111 -0
  64. data/sig/bucket_logging.rbs +76 -0
  65. data/sig/bucket_notification.rbs +114 -0
  66. data/sig/bucket_policy.rbs +59 -0
  67. data/sig/bucket_request_payment.rbs +54 -0
  68. data/sig/bucket_tagging.rbs +65 -0
  69. data/sig/bucket_versioning.rbs +77 -0
  70. data/sig/bucket_website.rbs +93 -0
  71. data/sig/client.rbs +2381 -0
  72. data/sig/customizations/bucket.rbs +19 -0
  73. data/sig/customizations/object.rbs +38 -0
  74. data/sig/customizations/object_summary.rbs +35 -0
  75. data/sig/errors.rbs +34 -0
  76. data/sig/multipart_upload.rbs +110 -0
  77. data/sig/multipart_upload_part.rbs +105 -0
  78. data/sig/object.rbs +442 -0
  79. data/sig/object_acl.rbs +86 -0
  80. data/sig/object_summary.rbs +334 -0
  81. data/sig/object_version.rbs +137 -0
  82. data/sig/resource.rbs +127 -0
  83. data/sig/types.rbs +2568 -0
  84. data/sig/waiters.rbs +95 -0
  85. metadata +43 -11
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module Aws
6
+ module S3
7
+ # @api private
8
+ class ExpressCredentials
9
+ include CredentialProvider
10
+ include RefreshingCredentials
11
+
12
+ SYNC_EXPIRATION_LENGTH = 60 # 1 minute
13
+ ASYNC_EXPIRATION_LENGTH = 120 # 2 minutes
14
+
15
+ def initialize(options = {})
16
+ @client = options[:client]
17
+ @create_session_params = {}
18
+ options.each_pair do |key, value|
19
+ if self.class.create_session_options.include?(key)
20
+ @create_session_params[key] = value
21
+ end
22
+ end
23
+ @async_refresh = true
24
+ super
25
+ end
26
+
27
+ # @return [S3::Client]
28
+ attr_reader :client
29
+
30
+ private
31
+
32
+ def refresh
33
+ c = @client.create_session(@create_session_params).credentials
34
+ @credentials = Credentials.new(
35
+ c.access_key_id,
36
+ c.secret_access_key,
37
+ c.session_token
38
+ )
39
+ @expiration = c.expiration
40
+ end
41
+
42
+ class << self
43
+
44
+ # @api private
45
+ def create_session_options
46
+ @cso ||= begin
47
+ input = S3::Client.api.operation(:create_session).input
48
+ Set.new(input.shape.member_names)
49
+ end
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module S3
5
+ # @api private
6
+ def self.express_credentials_cache
7
+ @express_credentials_cache ||= LRUCache.new(max_entries: 100)
8
+ end
9
+
10
+ # Returns Credentials class for S3 Express. Accepts CreateSession
11
+ # params as options. See {Client#create_session} for details.
12
+ class ExpressCredentialsProvider
13
+ # @param [Hash] options
14
+ # @option options [Client] :client The S3 client used to create the
15
+ # session.
16
+ # @option options [String] :session_mode (see: {Client#create_session})
17
+ # @option options [Boolean] :caching (true) When true, credentials will
18
+ # be cached.
19
+ # @option options [Callable] :before_refresh Proc called before
20
+ # credentials are refreshed.
21
+ def initialize(options = {})
22
+ @client = options.delete(:client)
23
+ @caching = options.delete(:caching) != false
24
+ @options = options
25
+ return unless @caching
26
+
27
+ @cache = Aws::S3.express_credentials_cache
28
+ end
29
+
30
+ def express_credentials_for(bucket)
31
+ if @caching
32
+ cached_credentials_for(bucket)
33
+ else
34
+ new_credentials_for(bucket)
35
+ end
36
+ end
37
+
38
+ attr_accessor :client
39
+
40
+ private
41
+
42
+ def cached_credentials_for(bucket)
43
+ if @cache.key?(bucket)
44
+ @cache[bucket]
45
+ else
46
+ @cache[bucket] = new_credentials_for(bucket)
47
+ end
48
+ end
49
+
50
+ def new_credentials_for(bucket)
51
+ ExpressCredentials.new(
52
+ bucket: bucket,
53
+ client: @client,
54
+ **@options
55
+ )
56
+ end
57
+ end
58
+ end
59
+ end
@@ -32,39 +32,68 @@ module Aws
32
32
  }
33
33
  @params[:version_id] = options[:version_id] if options[:version_id]
34
34
 
35
- case @mode
36
- when 'auto' then multipart_download
37
- when 'single_request' then single_request
38
- when 'get_range'
39
- if @chunk_size
40
- resp = @client.head_object(@params)
41
- multithreaded_get_by_ranges(construct_chunks(resp.content_length))
35
+ # checksum_mode only supports the value "ENABLED"
36
+ # falsey values (false/nil) or "DISABLED" should be considered
37
+ # disabled and the api parameter should be unset.
38
+ if (checksum_mode = options.fetch(:checksum_mode, 'ENABLED'))
39
+ @params[:checksum_mode] = checksum_mode unless checksum_mode.upcase == 'DISABLED'
40
+ end
41
+ @on_checksum_validated = options[:on_checksum_validated]
42
+
43
+ @progress_callback = options[:progress_callback]
44
+
45
+ validate!
46
+
47
+ Aws::Plugins::UserAgent.metric('S3_TRANSFER') do
48
+ case @mode
49
+ when 'auto' then multipart_download
50
+ when 'single_request' then single_request
51
+ when 'get_range'
52
+ if @chunk_size
53
+ resp = @client.head_object(@params)
54
+ multithreaded_get_by_ranges(resp.content_length)
55
+ else
56
+ msg = 'In :get_range mode, :chunk_size must be provided'
57
+ raise ArgumentError, msg
58
+ end
42
59
  else
43
- msg = 'In :get_range mode, :chunk_size must be provided'
60
+ msg = "Invalid mode #{@mode} provided, "\
61
+ 'mode should be :single_request, :get_range or :auto'
44
62
  raise ArgumentError, msg
45
63
  end
46
- else
47
- msg = "Invalid mode #{@mode} provided, "\
48
- 'mode should be :single_request, :get_range or :auto'
49
- raise ArgumentError, msg
50
64
  end
51
65
  end
52
66
 
53
67
  private
54
68
 
69
+ def validate!
70
+ if @on_checksum_validated && @params[:checksum_mode] != 'ENABLED'
71
+ raise ArgumentError, "You must set checksum_mode: 'ENABLED' " +
72
+ "when providing a on_checksum_validated callback"
73
+ end
74
+
75
+ if @on_checksum_validated && !@on_checksum_validated.respond_to?(:call)
76
+ raise ArgumentError, 'on_checksum_validated must be callable'
77
+ end
78
+ end
79
+
55
80
  def multipart_download
56
81
  resp = @client.head_object(@params.merge(part_number: 1))
57
82
  count = resp.parts_count
58
83
  if count.nil? || count <= 1
59
- resp.content_length < MIN_CHUNK_SIZE ?
60
- single_request :
61
- multithreaded_get_by_ranges(construct_chunks(resp.content_length))
84
+ if resp.content_length <= MIN_CHUNK_SIZE
85
+ single_request
86
+ else
87
+ multithreaded_get_by_ranges(resp.content_length)
88
+ end
62
89
  else
63
90
  # partNumber is an option
64
91
  resp = @client.head_object(@params)
65
- resp.content_length < MIN_CHUNK_SIZE ?
66
- single_request :
92
+ if resp.content_length <= MIN_CHUNK_SIZE
93
+ single_request
94
+ else
67
95
  compute_mode(resp.content_length, count)
96
+ end
68
97
  end
69
98
  end
70
99
 
@@ -72,9 +101,9 @@ module Aws
72
101
  chunk_size = compute_chunk(file_size)
73
102
  part_size = (file_size.to_f / count.to_f).ceil
74
103
  if chunk_size < part_size
75
- multithreaded_get_by_ranges(construct_chunks(file_size))
104
+ multithreaded_get_by_ranges(file_size)
76
105
  else
77
- multithreaded_get_by_parts(count)
106
+ multithreaded_get_by_parts(count, file_size)
78
107
  end
79
108
  end
80
109
 
@@ -82,10 +111,11 @@ module Aws
82
111
  offset = 0
83
112
  default_chunk_size = compute_chunk(file_size)
84
113
  chunks = []
85
- while offset <= file_size
114
+ while offset < file_size
86
115
  progress = offset + default_chunk_size
87
- chunks << "bytes=#{offset}-#{progress < file_size ? progress : file_size}"
88
- offset = progress + 1
116
+ progress = file_size if progress > file_size
117
+ chunks << "bytes=#{offset}-#{progress - 1}"
118
+ offset = progress
89
119
  end
90
120
  chunks
91
121
  end
@@ -94,12 +124,9 @@ module Aws
94
124
  if @chunk_size && @chunk_size > file_size
95
125
  raise ArgumentError, ":chunk_size shouldn't exceed total file size."
96
126
  else
97
- chunk_size = @chunk_size || [
98
- (file_size.to_f / MAX_PARTS).ceil,
99
- MIN_CHUNK_SIZE
127
+ @chunk_size || [
128
+ (file_size.to_f / MAX_PARTS).ceil, MIN_CHUNK_SIZE
100
129
  ].max.to_i
101
- chunk_size -= 1 if file_size % chunk_size == 1
102
- chunk_size
103
130
  end
104
131
  end
105
132
 
@@ -108,27 +135,64 @@ module Aws
108
135
  chunks.each_slice(@thread_count).to_a
109
136
  end
110
137
 
111
- def multithreaded_get_by_ranges(chunks)
112
- thread_batches(chunks, 'range')
138
+ def multithreaded_get_by_ranges(file_size)
139
+ offset = 0
140
+ default_chunk_size = compute_chunk(file_size)
141
+ chunks = []
142
+ part_number = 1 # parts start at 1
143
+ while offset < file_size
144
+ progress = offset + default_chunk_size
145
+ progress = file_size if progress > file_size
146
+ range = "bytes=#{offset}-#{progress - 1}"
147
+ chunks << Part.new(
148
+ part_number: part_number,
149
+ size: (progress-offset),
150
+ params: @params.merge(range: range)
151
+ )
152
+ part_number += 1
153
+ offset = progress
154
+ end
155
+ download_in_threads(PartList.new(chunks), file_size)
113
156
  end
114
157
 
115
- def multithreaded_get_by_parts(parts)
116
- thread_batches(parts, 'part_number')
158
+ def multithreaded_get_by_parts(n_parts, total_size)
159
+ parts = (1..n_parts).map do |part|
160
+ Part.new(part_number: part, params: @params.merge(part_number: part))
161
+ end
162
+ download_in_threads(PartList.new(parts), total_size)
117
163
  end
118
164
 
119
- def thread_batches(chunks, param)
120
- batches(chunks, param).each do |batch|
121
- threads = []
122
- batch.each do |chunk|
123
- threads << Thread.new do
124
- resp = @client.get_object(
125
- @params.merge(param.to_sym => chunk)
126
- )
127
- write(resp)
165
+ def download_in_threads(pending, total_size)
166
+ threads = []
167
+ if @progress_callback
168
+ progress = MultipartProgress.new(pending, total_size, @progress_callback)
169
+ end
170
+ @thread_count.times do
171
+ thread = Thread.new do
172
+ begin
173
+ while part = pending.shift
174
+ if progress
175
+ part.params[:on_chunk_received] =
176
+ proc do |_chunk, bytes, total|
177
+ progress.call(part.part_number, bytes, total)
178
+ end
179
+ end
180
+ resp = @client.get_object(part.params)
181
+ write(resp)
182
+ if @on_checksum_validated && resp.checksum_validated
183
+ @on_checksum_validated.call(resp.checksum_validated, resp)
184
+ end
185
+ end
186
+ nil
187
+ rescue => error
188
+ # keep other threads from downloading other parts
189
+ pending.clear!
190
+ raise error
128
191
  end
129
192
  end
130
- threads.each(&:join)
193
+ threads << thread
131
194
  end
195
+ threads.map(&:value).compact
132
196
  end
133
197
 
134
198
  def write(resp)
@@ -138,9 +202,70 @@ module Aws
138
202
  end
139
203
 
140
204
  def single_request
141
- @client.get_object(
142
- @params.merge(response_target: @path)
143
- )
205
+ params = @params.merge(response_target: @path)
206
+ params[:on_chunk_received] = single_part_progress if @progress_callback
207
+ resp = @client.get_object(params)
208
+
209
+ return resp unless @on_checksum_validated
210
+
211
+ if resp.checksum_validated
212
+ @on_checksum_validated.call(resp.checksum_validated, resp)
213
+ end
214
+
215
+ resp
216
+ end
217
+
218
+ def single_part_progress
219
+ proc do |_chunk, bytes_read, total_size|
220
+ @progress_callback.call([bytes_read], [total_size], total_size)
221
+ end
222
+ end
223
+
224
+ class Part < Struct.new(:part_number, :size, :params)
225
+ include Aws::Structure
226
+ end
227
+
228
+ # @api private
229
+ class PartList
230
+ include Enumerable
231
+ def initialize(parts = [])
232
+ @parts = parts
233
+ @mutex = Mutex.new
234
+ end
235
+
236
+ def shift
237
+ @mutex.synchronize { @parts.shift }
238
+ end
239
+
240
+ def size
241
+ @mutex.synchronize { @parts.size }
242
+ end
243
+
244
+ def clear!
245
+ @mutex.synchronize { @parts.clear }
246
+ end
247
+
248
+ def each(&block)
249
+ @mutex.synchronize { @parts.each(&block) }
250
+ end
251
+ end
252
+
253
+ # @api private
254
+ class MultipartProgress
255
+ def initialize(parts, total_size, progress_callback)
256
+ @bytes_received = Array.new(parts.size, 0)
257
+ @part_sizes = parts.map(&:size)
258
+ @total_size = total_size
259
+ @progress_callback = progress_callback
260
+ end
261
+
262
+ def call(part_number, bytes_received, total)
263
+ # part numbers start at 1
264
+ @bytes_received[part_number - 1] = bytes_received
265
+ # part size may not be known until we get the first response
266
+ @part_sizes[part_number - 1] ||= total
267
+ @progress_callback.call(@bytes_received, @part_sizes, @total_size)
268
+ end
144
269
  end
145
270
  end
146
271
  end
@@ -37,12 +37,14 @@ module Aws
37
37
  # objects smaller than the multipart threshold.
38
38
  # @return [void]
39
39
  def upload(source, options = {})
40
- if File.size(source) >= multipart_threshold
41
- MultipartFileUploader.new(@options).upload(source, options)
42
- else
43
- # remove multipart parameters not supported by put_object
44
- options.delete(:thread_count)
45
- put_object(source, options)
40
+ Aws::Plugins::UserAgent.metric('S3_TRANSFER') do
41
+ if File.size(source) >= multipart_threshold
42
+ MultipartFileUploader.new(@options).upload(source, options)
43
+ else
44
+ # remove multipart parameters not supported by put_object
45
+ options.delete(:thread_count)
46
+ put_object(source, options)
47
+ end
46
48
  end
47
49
  end
48
50
 
@@ -89,12 +89,13 @@ module Aws
89
89
  key: options[:key],
90
90
  upload_id: upload_id
91
91
  )
92
- msg = "multipart upload failed: #{errors.map(&:message).join("; ")}"
92
+ msg = "multipart upload failed: #{errors.map(&:message).join('; ')}"
93
93
  raise MultipartUploadError.new(msg, errors)
94
94
  rescue MultipartUploadError => error
95
95
  raise error
96
96
  rescue => error
97
- msg = "failed to abort multipart upload: #{error.message}"
97
+ msg = "failed to abort multipart upload: #{error.message}. "\
98
+ "Multipart upload failed: #{errors.map(&:message).join('; ')}"
98
99
  raise MultipartUploadError.new(msg, errors + [error])
99
100
  end
100
101
 
@@ -146,7 +147,7 @@ module Aws
146
147
  if (callback = options[:progress_callback])
147
148
  progress = MultipartProgress.new(pending, callback)
148
149
  end
149
- @thread_count.times do
150
+ options.fetch(:thread_count, @thread_count).times do
150
151
  thread = Thread.new do
151
152
  begin
152
153
  while part = pending.shift
@@ -175,7 +176,6 @@ module Aws
175
176
  error
176
177
  end
177
178
  end
178
- thread.abort_on_exception = true
179
179
  threads << thread
180
180
  end
181
181
  threads.map(&:value).compact
@@ -43,11 +43,14 @@ module Aws
43
43
 
44
44
  # @option options [required,String] :bucket
45
45
  # @option options [required,String] :key
46
+ # @option options [Integer] :thread_count (THREAD_COUNT)
46
47
  # @return [Seahorse::Client::Response] - the CompleteMultipartUploadResponse
47
48
  def upload(options = {}, &block)
48
- upload_id = initiate_upload(options)
49
- parts = upload_parts(upload_id, options, &block)
50
- complete_upload(upload_id, parts, options)
49
+ Aws::Plugins::UserAgent.metric('S3_TRANSFER') do
50
+ upload_id = initiate_upload(options)
51
+ parts = upload_parts(upload_id, options, &block)
52
+ complete_upload(upload_id, parts, options)
53
+ end
51
54
  end
52
55
 
53
56
  private
@@ -99,12 +102,13 @@ module Aws
99
102
  key: options[:key],
100
103
  upload_id: upload_id
101
104
  )
102
- msg = "multipart upload failed: #{errors.map(&:message).join("; ")}"
105
+ msg = "multipart upload failed: #{errors.map(&:message).join('; ')}"
103
106
  raise MultipartUploadError.new(msg, errors)
104
107
  rescue MultipartUploadError => error
105
108
  raise error
106
109
  rescue => error
107
- msg = "failed to abort multipart upload: #{error.message}"
110
+ msg = "failed to abort multipart upload: #{error.message}. "\
111
+ "Multipart upload failed: #{errors.map(&:message).join('; ')}"
108
112
  raise MultipartUploadError.new(msg, errors + [error])
109
113
  end
110
114
 
@@ -149,7 +153,7 @@ module Aws
149
153
  def upload_in_threads(read_pipe, completed, options, thread_errors)
150
154
  mutex = Mutex.new
151
155
  part_number = 0
152
- @thread_count.times.map do
156
+ options.fetch(:thread_count, @thread_count).times.map do
153
157
  thread = Thread.new do
154
158
  begin
155
159
  loop do
@@ -190,7 +194,6 @@ module Aws
190
194
  error
191
195
  end
192
196
  end
193
- thread.abort_on_exception = true
194
197
  thread
195
198
  end
196
199
  end