aws-sdk-s3 1.220.0 → 1.232.2

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +89 -0
  3. data/VERSION +1 -1
  4. data/lib/aws-sdk-s3/bucket.rb +26 -2
  5. data/lib/aws-sdk-s3/bucket_notification.rb +3 -3
  6. data/lib/aws-sdk-s3/client.rb +869 -62
  7. data/lib/aws-sdk-s3/client_api.rb +301 -0
  8. data/lib/aws-sdk-s3/customizations/object.rb +55 -25
  9. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +14 -1
  10. data/lib/aws-sdk-s3/encryptionV3/kms_cipher_provider.rb +13 -1
  11. data/lib/aws-sdk-s3/endpoint_provider.rb +6 -3
  12. data/lib/aws-sdk-s3/endpoints.rb +68 -0
  13. data/lib/aws-sdk-s3/errors.rb +66 -0
  14. data/lib/aws-sdk-s3/file_downloader.rb +3 -1
  15. data/lib/aws-sdk-s3/multipart_file_uploader.rb +1 -1
  16. data/lib/aws-sdk-s3/object.rb +150 -8
  17. data/lib/aws-sdk-s3/object_copier.rb +11 -22
  18. data/lib/aws-sdk-s3/object_multipart_copier.rb +177 -84
  19. data/lib/aws-sdk-s3/object_summary.rb +117 -8
  20. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +1 -1
  21. data/lib/aws-sdk-s3/transfer_manager.rb +44 -31
  22. data/lib/aws-sdk-s3/types.rb +1162 -46
  23. data/lib/aws-sdk-s3.rb +1 -1
  24. data/sig/bucket.rbs +7 -4
  25. data/sig/bucket_acl.rbs +1 -1
  26. data/sig/bucket_cors.rbs +1 -1
  27. data/sig/bucket_lifecycle.rbs +1 -1
  28. data/sig/bucket_lifecycle_configuration.rbs +4 -4
  29. data/sig/bucket_logging.rbs +1 -1
  30. data/sig/bucket_notification.rbs +9 -9
  31. data/sig/bucket_tagging.rbs +1 -1
  32. data/sig/bucket_website.rbs +1 -1
  33. data/sig/client.rbs +246 -198
  34. data/sig/errors.rbs +12 -0
  35. data/sig/multipart_upload.rbs +2 -2
  36. data/sig/object.rbs +32 -13
  37. data/sig/object_acl.rbs +1 -1
  38. data/sig/object_summary.rbs +22 -12
  39. data/sig/params.rbs +78 -0
  40. data/sig/resource.rbs +1 -1
  41. data/sig/types.rbs +249 -39
  42. metadata +4 -3
@@ -1,20 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'thread'
4
- require "cgi/escape"
5
- require "cgi/util" if RUBY_VERSION < "3.5"
3
+ require 'cgi/escape'
4
+ require 'cgi/util' if RUBY_VERSION < '3.5'
5
+ require 'set'
6
6
 
7
7
  module Aws
8
8
  module S3
9
9
  # @api private
10
- class ObjectMultipartCopier
11
-
12
- FIVE_MB = 5 * 1024 * 1024 # 5MB
13
-
14
- FILE_TOO_SMALL = "unable to multipart copy files smaller than 5MB"
15
-
10
+ class ObjectMultipartCopier # rubocop:disable Metrics/ClassLength
11
+ MIN_PART_SIZE = 5 * 1024 * 1024 # 5MB
16
12
  MAX_PARTS = 10_000
17
13
 
14
+ API_OPTIONS = {
15
+ create_multipart_upload: Set.new(Client.api.operation(:create_multipart_upload).input.shape.member_names),
16
+ upload_part_copy: Set.new(Client.api.operation(:upload_part_copy).input.shape.member_names),
17
+ complete_multipart_upload: Set.new(Client.api.operation(:complete_multipart_upload).input.shape.member_names)
18
+ }.freeze
19
+
18
20
  # @option options [Client] :client
19
21
  # @option options [Integer] :min_part_size (52428800)
20
22
  # Size of copied parts. Defaults to 50MB.
@@ -29,29 +31,115 @@ module Aws
29
31
  def initialize(options = {})
30
32
  @use_source_parts = options.delete(:use_source_parts) || false
31
33
  @thread_count = options.delete(:thread_count) || 10
32
- @min_part_size = options.delete(:min_part_size) || (FIVE_MB * 10)
34
+ @min_part_size = options.delete(:min_part_size) || (MIN_PART_SIZE * 10)
33
35
  @client = options[:client] || Client.new
36
+ @source_client = nil
37
+ @source = nil
38
+ @source_etag = nil
39
+ @source_parts_count = nil
40
+ @first_part_size = nil
34
41
  end
35
42
 
36
43
  # @return [Client]
37
44
  attr_reader :client
38
45
 
39
46
  # @option (see S3::Client#copy_object)
40
- def copy(options = {})
41
- metadata = source_metadata(options)
47
+ def copy(options = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
48
+ @source_client = options[:copy_source_client] || @client
49
+ @source = resolve_source(options[:copy_source])
50
+ metadata = resolve_metadata(options)
42
51
  size = metadata[:content_length]
43
- options[:upload_id] = initiate_upload(metadata.merge(options))
52
+ @source_etag = metadata[:etag]
53
+ @source[:version_id] ||= metadata[:version_id]
54
+ resolve_source_parts
55
+ tag_set = resolve_tags(options)
56
+ annotations = resolve_annotations(options)
57
+
58
+ create_opts = resolve_create_opts(metadata, options)
59
+ options[:upload_id] = initiate_upload(create_opts)
60
+
44
61
  begin
45
62
  parts = copy_parts(size, default_part_size(size), options)
46
- complete_upload(parts, options)
47
- rescue => error
63
+ resp = complete_upload(parts, options)
64
+ rescue StandardError => e
48
65
  abort_upload(options)
49
- raise error
66
+ raise e
50
67
  end
68
+
69
+ put_tags(tag_set, resp, options) if tag_set
70
+ put_annotations(annotations, resp, options) if annotations&.any?
71
+ resp
51
72
  end
52
73
 
53
74
  private
54
75
 
76
+ def resolve_source(copy_source)
77
+ bucket, remaining = copy_source.split('/', 2)
78
+ key, version_id = remaining.split('?versionId=', 2)
79
+ result = { bucket: bucket, key: CGI.unescape(key) }
80
+ result[:version_id] = version_id if version_id
81
+ result
82
+ end
83
+
84
+ def resolve_metadata(options)
85
+ return options.slice(:content_length) if options[:content_length]
86
+
87
+ resp = @source_client.head_object(@source).to_h
88
+ resp.delete(:server_side_encryption)
89
+ resp.delete(:ssekms_key_id)
90
+ resp
91
+ end
92
+
93
+ def resolve_source_parts
94
+ return unless @use_source_parts
95
+
96
+ resp = @source_client.head_object(@source.merge(part_number: 1))
97
+ @source_parts_count = resp.parts_count
98
+ @first_part_size = resp.content_length if @source_parts_count
99
+ end
100
+
101
+ def resolve_create_opts(metadata, options)
102
+ return options if options[:metadata_directive] == 'REPLACE'
103
+
104
+ create_opts = metadata.merge(options)
105
+ create_opts.delete(:tagging) if options[:tags_directive]
106
+ create_opts
107
+ end
108
+
109
+ def resolve_tags(options) # rubocop:disable Metrics/MethodLength
110
+ return unless options[:tags_directive]
111
+
112
+ case options[:tags_directive]
113
+ when 'COPY'
114
+ resp = @source_client.get_object_tagging(@source)
115
+ resp.tag_set
116
+ when 'REPLACE'
117
+ tagging = options[:tagging]
118
+ return if tagging.nil? || tagging.empty?
119
+
120
+ tagging.split('&').map do |pair|
121
+ k, v = pair.split('=', 2)
122
+ { key: CGI.unescape(k), value: CGI.unescape(v) }
123
+ end
124
+ end
125
+ end
126
+
127
+ def resolve_annotations(options) # rubocop:disable Metrics/MethodLength
128
+ return unless options[:annotations_directive] == 'COPY'
129
+
130
+ list_resp = @source_client.list_object_annotations(@source)
131
+ return if list_resp.annotations.empty?
132
+
133
+ list_resp.annotations.map do |annotation|
134
+ {
135
+ name: annotation.annotation_name,
136
+ payload: @source_client.get_object_annotation(
137
+ @source.merge(annotation_name: annotation.annotation_name)
138
+ ).annotation_payload
139
+ }
140
+ end
141
+ end
142
+
55
143
  def initiate_upload(options)
56
144
  options = options_for(:create_multipart_upload, options)
57
145
  @client.create_multipart_upload(options).upload_id
@@ -63,28 +151,30 @@ module Aws
63
151
  @thread_count.times do
64
152
  threads << copy_part_thread(queue)
65
153
  end
66
- threads.map(&:value).flatten.sort_by{ |part| part[:part_number] }
154
+ threads.map(&:value).flatten.sort_by { |part| part[:part_number] }
67
155
  end
68
156
 
69
- def copy_part_thread(queue)
157
+ def copy_part_thread(queue) # rubocop:disable Metrics/MethodLength
70
158
  Thread.new do
71
- begin
159
+ begin # rubocop:disable Style/RedundantBegin
72
160
  completed = []
73
- while part = queue.shift
161
+ while (part = queue.shift)
74
162
  completed << copy_part(part)
75
163
  end
76
164
  completed
77
- rescue => error
165
+ rescue StandardError => e
78
166
  queue.clear!
79
- raise error
167
+ raise e
80
168
  end
81
169
  end
82
170
  end
83
171
 
84
172
  def copy_part(part)
85
- @client.upload_part_copy(part).copy_part_result.to_h.merge({
86
- part_number: part[:part_number]
87
- }).tap { |result| result.delete(:last_modified) }
173
+ @client.upload_part_copy(part)
174
+ .copy_part_result
175
+ .to_h
176
+ .merge({ part_number: part[:part_number] })
177
+ .tap { |result| result.delete(:last_modified) }
88
178
  end
89
179
 
90
180
  def complete_upload(parts, options)
@@ -94,100 +184,104 @@ module Aws
94
184
  end
95
185
 
96
186
  def abort_upload(options)
97
- @client.abort_multipart_upload({
98
- bucket: options[:bucket],
99
- key: options[:key],
100
- upload_id: options[:upload_id],
101
- })
187
+ @client.abort_multipart_upload(
188
+ {
189
+ bucket: options[:bucket],
190
+ key: options[:key],
191
+ upload_id: options[:upload_id]
192
+ }
193
+ )
102
194
  end
103
195
 
104
- def compute_parts(size, default_part_size, options)
196
+ def compute_parts(size, default_part_size, options) # rubocop:disable Metrics/MethodLength
105
197
  part_number = 1
106
198
  offset = 0
107
199
  parts = []
108
200
  options = options_for(:upload_part_copy, options)
201
+ options[:copy_source_if_match] = @source_etag if @source_etag
109
202
  while offset < size
110
203
  part_size = calculate_part_size(part_number, default_part_size, options)
111
- parts << options.merge({
112
- part_number: part_number,
113
- copy_source_range: byte_range(offset, part_size, size),
114
- })
204
+ parts << options.merge({ part_number: part_number, copy_source_range: byte_range(offset, part_size, size) })
115
205
  part_number += 1
116
206
  offset += part_size
117
207
  end
118
208
  parts
119
209
  end
120
210
 
121
- def byte_range(offset, part_size, size)
122
- if offset + part_size < size
123
- "bytes=#{offset}-#{offset + part_size - 1}"
124
- else
125
- "bytes=#{offset}-#{size - 1}"
126
- end
127
- end
128
-
129
- def calculate_part_size(part_number, default_part_size, options)
130
- if @use_source_parts && source_has_parts(options)
131
- source_metadata(options.merge({ part_number: part_number }))[:content_length]
211
+ def calculate_part_size(part_number, default_part_size, _options)
212
+ if @source_parts_count
213
+ resolve_part_size(part_number)
132
214
  else
133
215
  default_part_size
134
216
  end
135
217
  end
136
218
 
137
- def source_has_parts(options)
138
- @source_has_parts ||= source_metadata(options.merge({ part_number: 1 }))[:parts_count]
139
- end
140
-
141
- def source_metadata(options)
142
- return options.slice(:content_length) if options[:content_length]
219
+ def resolve_part_size(part_number)
220
+ return @first_part_size if part_number == 1 && @first_part_size
143
221
 
144
- client = options[:copy_source_client] || @client
222
+ @source_client.head_object(@source.merge(part_number: part_number)).content_length
223
+ end
145
224
 
146
- if vid_match = options[:copy_source].match(/([^\/]+?)\/(.+)\?versionId=(.+)/)
147
- bucket, key, version_id = vid_match[1,3]
225
+ def byte_range(offset, part_size, size)
226
+ if offset + part_size < size
227
+ "bytes=#{offset}-#{offset + part_size - 1}"
148
228
  else
149
- bucket, key = options[:copy_source].match(/([^\/]+?)\/(.+)/)[1,2]
229
+ "bytes=#{offset}-#{size - 1}"
150
230
  end
231
+ end
151
232
 
152
- head_opts = { bucket: bucket, key: CGI.unescape(key) }.tap { |opts|
153
- opts[:version_id] = version_id if version_id
154
- opts[:part_number] = options[:part_number] if options[:part_number]
155
- }
233
+ def default_part_size(source_size)
234
+ raise ArgumentError, 'unable to multipart copy files smaller than 5MB' if source_size < MIN_PART_SIZE
156
235
 
157
- client.head_object(head_opts).to_h.tap { |head|
158
- head.delete(:server_side_encryption)
159
- head.delete(:ssekms_key_id)
160
- }
236
+ [(source_size.to_f / MAX_PARTS).ceil, @min_part_size].max.to_i
161
237
  end
162
238
 
163
- def default_part_size(source_size)
164
- if source_size < FIVE_MB
165
- raise ArgumentError, FILE_TOO_SMALL
166
- else
167
- [(source_size.to_f / MAX_PARTS).ceil, @min_part_size].max.to_i
239
+ def put_tags(tags, resp, options)
240
+ @client.put_object_tagging(
241
+ bucket: options[:bucket],
242
+ key: options[:key],
243
+ tagging: { tag_set: tags },
244
+ version_id: resp.version_id
245
+ )
246
+ rescue StandardError => e
247
+ raise StandardError,
248
+ 'Object data copied successfully. ' \
249
+ "Failed to copy tags: #{e.message}"
250
+ end
251
+
252
+ def put_annotations(annotations, response, options) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
253
+ succeeded = []
254
+ failed = []
255
+
256
+ annotations.each do |annotation|
257
+ @client.put_object_annotation(
258
+ bucket: options[:bucket],
259
+ key: options[:key],
260
+ version_id: response.version_id,
261
+ object_if_match: response.etag,
262
+ annotation_name: annotation[:name],
263
+ annotation_payload: annotation[:payload]
264
+ )
265
+ succeeded << annotation[:name]
266
+ rescue StandardError => e
267
+ failed << { key: annotation[:name], error: e }
168
268
  end
269
+ return if failed.empty?
270
+
271
+ raise StandardError,
272
+ 'Object data copied successfully. Failed to copy annotations: ' \
273
+ "#{failed.map { |f| "#{f[:key]} (#{f[:error].message})" }.join(', ')}. " \
274
+ "Succeeded: #{succeeded.join(', ')}"
169
275
  end
170
276
 
171
277
  def options_for(operation_name, options)
172
- API_OPTIONS[operation_name].inject({}) do |hash, opt_name|
278
+ API_OPTIONS[operation_name].each_with_object({}) do |opt_name, hash|
173
279
  hash[opt_name] = options[opt_name] if options.key?(opt_name)
174
- hash
175
280
  end
176
281
  end
177
282
 
178
- # @api private
179
- def self.options_for(shape_name)
180
- Client.api.metadata['shapes'][shape_name].member_names
181
- end
182
-
183
- API_OPTIONS = {
184
- create_multipart_upload: Types::CreateMultipartUploadRequest.members,
185
- upload_part_copy: Types::UploadPartCopyRequest.members,
186
- complete_multipart_upload: Types::CompleteMultipartUploadRequest.members,
187
- }
188
-
283
+ # A thread-safe work queue of part definitions for a multipart copy.
189
284
  class PartQueue
190
-
191
285
  def initialize(parts = [])
192
286
  @parts = parts
193
287
  @mutex = Mutex.new
@@ -200,7 +294,6 @@ module Aws
200
294
  def clear!
201
295
  @mutex.synchronize { @parts.clear }
202
296
  end
203
-
204
297
  end
205
298
  end
206
299
  end
@@ -361,8 +361,9 @@ module Aws::S3
361
361
  # },
362
362
  # metadata_directive: "COPY", # accepts COPY, REPLACE
363
363
  # tagging_directive: "COPY", # accepts COPY, REPLACE
364
- # server_side_encryption: "AES256", # accepts AES256, aws:fsx, aws:kms, aws:kms:dsse
365
- # storage_class: "STANDARD", # accepts STANDARD, REDUCED_REDUNDANCY, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER, DEEP_ARCHIVE, OUTPOSTS, GLACIER_IR, SNOW, EXPRESS_ONEZONE, FSX_OPENZFS, FSX_ONTAP
364
+ # annotation_directive: "COPY", # accepts COPY, EXCLUDE
365
+ # server_side_encryption: "AES256", # accepts AES256, aws:fsx, aws:backup, aws:kms, aws:kms:dsse
366
+ # storage_class: "STANDARD", # accepts STANDARD, REDUCED_REDUNDANCY, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER, DEEP_ARCHIVE, OUTPOSTS, GLACIER_IR, SNOW, EXPRESS_ONEZONE, FSX_OPENZFS, FSX_ONTAP, AWS_BACKUP_WARM, AWS_BACKUP_LOW_COST_WARM
366
367
  # website_redirect_location: "WebsiteRedirectLocation",
367
368
  # sse_customer_algorithm: "SSECustomerAlgorithm",
368
369
  # sse_customer_key: "SSECustomerKey",
@@ -378,6 +379,9 @@ module Aws::S3
378
379
  # object_lock_mode: "GOVERNANCE", # accepts GOVERNANCE, COMPLIANCE
379
380
  # object_lock_retain_until_date: Time.now,
380
381
  # object_lock_legal_hold_status: "ON", # accepts ON, OFF
382
+ # object_lock_event_hold: "ON", # accepts ON, OFF
383
+ # object_lock_event_hold_duration_days: 1,
384
+ # object_lock_event_hold_duration_years: 1,
381
385
  # expected_bucket_owner: "AccountId",
382
386
  # expected_source_bucket_owner: "AccountId",
383
387
  # })
@@ -711,6 +715,42 @@ module Aws::S3
711
715
  # value of `x-amz-tagging` is the empty value.
712
716
  #
713
717
  # </note>
718
+ # @option options [String] :annotation_directive
719
+ # Specifies whether you want to copy annotations from the source object
720
+ # or exclude them. If this header isn't specified, `COPY` is the
721
+ # default behavior.
722
+ #
723
+ # Valid Values: `COPY | EXCLUDE`
724
+ #
725
+ # You can specify this directive as either an HTTP header
726
+ # (`x-amz-object-annotation-directive`) or as a query string parameter.
727
+ # Use the query string form when generating presigned URLs that need to
728
+ # control annotation copy behavior.
729
+ #
730
+ # When set to `COPY`, you must have `s3:GetObjectAnnotation` permission
731
+ # on the source object and `s3:PutObjectAnnotation` permission on the
732
+ # destination. Each annotation copied is billed as a separate PUT
733
+ # request. If annotations on the source are modified during the copy,
734
+ # Amazon S3 returns a retryable error.
735
+ #
736
+ # <note markdown="1"> For directory buckets, annotations are not supported. Use `EXCLUDE` to
737
+ # copy objects to directory buckets without errors. If you specify
738
+ # `COPY` for a directory bucket, the request returns HTTP 501 (Not
739
+ # Implemented).
740
+ #
741
+ # </note>
742
+ #
743
+ # <note markdown="1"> When you copy objects using multipart upload (for example, when the
744
+ # Amazon Web Services CLI or Amazon Web Services SDKs use Transfer
745
+ # Manager for objects larger than approximately 8 MB), annotations are
746
+ # not copied by default. To include annotations, specify `--copy-props
747
+ # default` in the Amazon Web Services CLI or the equivalent SDK
748
+ # configuration. With this opt-in, the SDK reads source annotations,
749
+ # completes the multipart upload, and then writes each annotation to the
750
+ # destination. Between the upload completion and the last annotation
751
+ # write, the destination object exists without all its annotations.
752
+ #
753
+ # </note>
714
754
  # @option options [String] :server_side_encryption
715
755
  # The server-side encryption algorithm used when storing this object in
716
756
  # Amazon S3. Unrecognized or unsupported values won’t write a
@@ -1083,6 +1123,27 @@ module Aws::S3
1083
1123
  # <note markdown="1"> This functionality is not supported for directory buckets.
1084
1124
  #
1085
1125
  # </note>
1126
+ # @option options [String] :object_lock_event_hold
1127
+ # The event hold status to apply to the object copy. Set to `ON` to
1128
+ # enable or `OFF` to disable.
1129
+ #
1130
+ # <note markdown="1"> This functionality is not supported for directory buckets.
1131
+ #
1132
+ # </note>
1133
+ # @option options [Integer] :object_lock_event_hold_duration_days
1134
+ # The event hold duration in days to apply to the object copy. You
1135
+ # cannot specify a duration in both days and years.
1136
+ #
1137
+ # <note markdown="1"> This functionality is not supported for directory buckets.
1138
+ #
1139
+ # </note>
1140
+ # @option options [Integer] :object_lock_event_hold_duration_years
1141
+ # The event hold duration in years to apply to the object copy. You
1142
+ # cannot specify a duration in both days and years.
1143
+ #
1144
+ # <note markdown="1"> This functionality is not supported for directory buckets.
1145
+ #
1146
+ # </note>
1086
1147
  # @option options [String] :expected_bucket_owner
1087
1148
  # The account ID of the expected destination bucket owner. If the
1088
1149
  # account ID that you provide does not match the actual owner of the
@@ -1485,8 +1546,8 @@ module Aws::S3
1485
1546
  # metadata: {
1486
1547
  # "MetadataKey" => "MetadataValue",
1487
1548
  # },
1488
- # server_side_encryption: "AES256", # accepts AES256, aws:fsx, aws:kms, aws:kms:dsse
1489
- # storage_class: "STANDARD", # accepts STANDARD, REDUCED_REDUNDANCY, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER, DEEP_ARCHIVE, OUTPOSTS, GLACIER_IR, SNOW, EXPRESS_ONEZONE, FSX_OPENZFS, FSX_ONTAP
1549
+ # server_side_encryption: "AES256", # accepts AES256, aws:fsx, aws:backup, aws:kms, aws:kms:dsse
1550
+ # storage_class: "STANDARD", # accepts STANDARD, REDUCED_REDUNDANCY, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER, DEEP_ARCHIVE, OUTPOSTS, GLACIER_IR, SNOW, EXPRESS_ONEZONE, FSX_OPENZFS, FSX_ONTAP, AWS_BACKUP_WARM, AWS_BACKUP_LOW_COST_WARM
1490
1551
  # website_redirect_location: "WebsiteRedirectLocation",
1491
1552
  # sse_customer_algorithm: "SSECustomerAlgorithm",
1492
1553
  # sse_customer_key: "SSECustomerKey",
@@ -1499,6 +1560,9 @@ module Aws::S3
1499
1560
  # object_lock_mode: "GOVERNANCE", # accepts GOVERNANCE, COMPLIANCE
1500
1561
  # object_lock_retain_until_date: Time.now,
1501
1562
  # object_lock_legal_hold_status: "ON", # accepts ON, OFF
1563
+ # object_lock_event_hold: "ON", # accepts ON, OFF
1564
+ # object_lock_event_hold_duration_days: 1,
1565
+ # object_lock_event_hold_duration_years: 1,
1502
1566
  # expected_bucket_owner: "AccountId",
1503
1567
  # checksum_algorithm: "CRC32", # accepts CRC32, CRC32C, SHA1, SHA256, CRC64NVME, SHA512, MD5, XXHASH64, XXHASH3, XXHASH128
1504
1568
  # checksum_type: "COMPOSITE", # accepts COMPOSITE, FULL_OBJECT
@@ -2020,6 +2084,27 @@ module Aws::S3
2020
2084
  # <note markdown="1"> This functionality is not supported for directory buckets.
2021
2085
  #
2022
2086
  # </note>
2087
+ # @option options [String] :object_lock_event_hold
2088
+ # Specifies the event hold status to apply to the uploaded object. Set
2089
+ # to `ON` to enable or `OFF` to disable.
2090
+ #
2091
+ # <note markdown="1"> This functionality is not supported for directory buckets.
2092
+ #
2093
+ # </note>
2094
+ # @option options [Integer] :object_lock_event_hold_duration_days
2095
+ # Specifies the event hold duration in days to apply to the uploaded
2096
+ # object. You cannot specify a duration in both days and years.
2097
+ #
2098
+ # <note markdown="1"> This functionality is not supported for directory buckets.
2099
+ #
2100
+ # </note>
2101
+ # @option options [Integer] :object_lock_event_hold_duration_years
2102
+ # Specifies the event hold duration in years to apply to the uploaded
2103
+ # object. You cannot specify a duration in both days and years.
2104
+ #
2105
+ # <note markdown="1"> This functionality is not supported for directory buckets.
2106
+ #
2107
+ # </note>
2023
2108
  # @option options [String] :expected_bucket_owner
2024
2109
  # The account ID of the expected bucket owner. If the account ID that
2025
2110
  # you provide does not match the actual owner of the bucket, the request
@@ -2091,8 +2176,8 @@ module Aws::S3
2091
2176
  # metadata: {
2092
2177
  # "MetadataKey" => "MetadataValue",
2093
2178
  # },
2094
- # server_side_encryption: "AES256", # accepts AES256, aws:fsx, aws:kms, aws:kms:dsse
2095
- # storage_class: "STANDARD", # accepts STANDARD, REDUCED_REDUNDANCY, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER, DEEP_ARCHIVE, OUTPOSTS, GLACIER_IR, SNOW, EXPRESS_ONEZONE, FSX_OPENZFS, FSX_ONTAP
2179
+ # server_side_encryption: "AES256", # accepts AES256, aws:fsx, aws:backup, aws:kms, aws:kms:dsse
2180
+ # storage_class: "STANDARD", # accepts STANDARD, REDUCED_REDUNDANCY, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER, DEEP_ARCHIVE, OUTPOSTS, GLACIER_IR, SNOW, EXPRESS_ONEZONE, FSX_OPENZFS, FSX_ONTAP, AWS_BACKUP_WARM, AWS_BACKUP_LOW_COST_WARM
2096
2181
  # website_redirect_location: "WebsiteRedirectLocation",
2097
2182
  # sse_customer_algorithm: "SSECustomerAlgorithm",
2098
2183
  # sse_customer_key: "SSECustomerKey",
@@ -2105,6 +2190,9 @@ module Aws::S3
2105
2190
  # object_lock_mode: "GOVERNANCE", # accepts GOVERNANCE, COMPLIANCE
2106
2191
  # object_lock_retain_until_date: Time.now,
2107
2192
  # object_lock_legal_hold_status: "ON", # accepts ON, OFF
2193
+ # object_lock_event_hold: "ON", # accepts ON, OFF
2194
+ # object_lock_event_hold_duration_days: 1,
2195
+ # object_lock_event_hold_duration_years: 1,
2108
2196
  # expected_bucket_owner: "AccountId",
2109
2197
  # })
2110
2198
  # @param [Hash] options ({})
@@ -2730,6 +2818,27 @@ module Aws::S3
2730
2818
  #
2731
2819
  #
2732
2820
  # [1]: https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock.html
2821
+ # @option options [String] :object_lock_event_hold
2822
+ # Specifies the event hold status to apply to this object. Set to `ON`
2823
+ # to enable or `OFF` to disable.
2824
+ #
2825
+ # <note markdown="1"> This functionality is not supported for directory buckets.
2826
+ #
2827
+ # </note>
2828
+ # @option options [Integer] :object_lock_event_hold_duration_days
2829
+ # Specifies the event hold duration in days to apply to this object. You
2830
+ # cannot specify a duration in both days and years.
2831
+ #
2832
+ # <note markdown="1"> This functionality is not supported for directory buckets.
2833
+ #
2834
+ # </note>
2835
+ # @option options [Integer] :object_lock_event_hold_duration_years
2836
+ # Specifies the event hold duration in years to apply to this object.
2837
+ # You cannot specify a duration in both days and years.
2838
+ #
2839
+ # <note markdown="1"> This functionality is not supported for directory buckets.
2840
+ #
2841
+ # </note>
2733
2842
  # @option options [String] :expected_bucket_owner
2734
2843
  # The account ID of the expected bucket owner. If the account ID that
2735
2844
  # you provide does not match the actual owner of the bucket, the request
@@ -2796,7 +2905,7 @@ module Aws::S3
2796
2905
  # bucket_name: "BucketName", # required
2797
2906
  # prefix: "LocationPrefix", # required
2798
2907
  # encryption: {
2799
- # encryption_type: "AES256", # required, accepts AES256, aws:fsx, aws:kms, aws:kms:dsse
2908
+ # encryption_type: "AES256", # required, accepts AES256, aws:fsx, aws:backup, aws:kms, aws:kms:dsse
2800
2909
  # kms_key_id: "SSEKMSKeyId",
2801
2910
  # kms_context: "KMSContext",
2802
2911
  # },
@@ -2827,7 +2936,7 @@ module Aws::S3
2827
2936
  # value: "MetadataValue",
2828
2937
  # },
2829
2938
  # ],
2830
- # storage_class: "STANDARD", # accepts STANDARD, REDUCED_REDUNDANCY, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER, DEEP_ARCHIVE, OUTPOSTS, GLACIER_IR, SNOW, EXPRESS_ONEZONE, FSX_OPENZFS, FSX_ONTAP
2939
+ # storage_class: "STANDARD", # accepts STANDARD, REDUCED_REDUNDANCY, STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING, GLACIER, DEEP_ARCHIVE, OUTPOSTS, GLACIER_IR, SNOW, EXPRESS_ONEZONE, FSX_OPENZFS, FSX_ONTAP, AWS_BACKUP_WARM, AWS_BACKUP_LOW_COST_WARM
2831
2940
  # },
2832
2941
  # },
2833
2942
  # },
@@ -19,7 +19,7 @@ module Aws
19
19
  private_constant :ENCODINGS_TO_FIX
20
20
 
21
21
  # A regular expression to match detect errors in the response body
22
- ERROR_PATTERN = /<\?xml\s[^>]*\?>\s*<Error>/.freeze
22
+ ERROR_PATTERN = /\A\s*(<\?xml[^>]*\?>\s*)?<Error>/.freeze
23
23
  private_constant :ERROR_PATTERN
24
24
 
25
25
  # A regular expression to match an error message in the response body