aws-sdk-s3 1.220.0 → 1.232.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +79 -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 +21 -0
  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 +176 -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/types.rb +1162 -46
  22. data/lib/aws-sdk-s3.rb +1 -1
  23. data/sig/bucket.rbs +7 -4
  24. data/sig/bucket_acl.rbs +1 -1
  25. data/sig/bucket_cors.rbs +1 -1
  26. data/sig/bucket_lifecycle.rbs +1 -1
  27. data/sig/bucket_lifecycle_configuration.rbs +4 -4
  28. data/sig/bucket_logging.rbs +1 -1
  29. data/sig/bucket_notification.rbs +9 -9
  30. data/sig/bucket_tagging.rbs +1 -1
  31. data/sig/bucket_website.rbs +1 -1
  32. data/sig/client.rbs +246 -198
  33. data/sig/errors.rbs +12 -0
  34. data/sig/multipart_upload.rbs +2 -2
  35. data/sig/object.rbs +32 -13
  36. data/sig/object_acl.rbs +1 -1
  37. data/sig/object_summary.rbs +22 -12
  38. data/sig/params.rbs +78 -0
  39. data/sig/resource.rbs +1 -1
  40. data/sig/types.rbs +249 -39
  41. 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,114 @@ 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?
51
71
  end
52
72
 
53
73
  private
54
74
 
75
+ def resolve_source(copy_source)
76
+ bucket, remaining = copy_source.split('/', 2)
77
+ key, version_id = remaining.split('?versionId=', 2)
78
+ result = { bucket: bucket, key: CGI.unescape(key) }
79
+ result[:version_id] = version_id if version_id
80
+ result
81
+ end
82
+
83
+ def resolve_metadata(options)
84
+ return options.slice(:content_length) if options[:content_length]
85
+
86
+ resp = @source_client.head_object(@source).to_h
87
+ resp.delete(:server_side_encryption)
88
+ resp.delete(:ssekms_key_id)
89
+ resp
90
+ end
91
+
92
+ def resolve_source_parts
93
+ return unless @use_source_parts
94
+
95
+ resp = @source_client.head_object(@source.merge(part_number: 1))
96
+ @source_parts_count = resp.parts_count
97
+ @first_part_size = resp.content_length if @source_parts_count
98
+ end
99
+
100
+ def resolve_create_opts(metadata, options)
101
+ return options if options[:metadata_directive] == 'REPLACE'
102
+
103
+ create_opts = metadata.merge(options)
104
+ create_opts.delete(:tagging) if options[:tags_directive]
105
+ create_opts
106
+ end
107
+
108
+ def resolve_tags(options) # rubocop:disable Metrics/MethodLength
109
+ return unless options[:tags_directive]
110
+
111
+ case options[:tags_directive]
112
+ when 'COPY'
113
+ resp = @source_client.get_object_tagging(@source)
114
+ resp.tag_set
115
+ when 'REPLACE'
116
+ tagging = options[:tagging]
117
+ return if tagging.nil? || tagging.empty?
118
+
119
+ tagging.split('&').map do |pair|
120
+ k, v = pair.split('=', 2)
121
+ { key: CGI.unescape(k), value: CGI.unescape(v) }
122
+ end
123
+ end
124
+ end
125
+
126
+ def resolve_annotations(options) # rubocop:disable Metrics/MethodLength
127
+ return unless options[:annotations_directive] == 'COPY'
128
+
129
+ list_resp = @source_client.list_object_annotations(@source)
130
+ return if list_resp.annotations.empty?
131
+
132
+ list_resp.annotations.map do |annotation|
133
+ {
134
+ name: annotation.annotation_name,
135
+ payload: @source_client.get_object_annotation(
136
+ @source.merge(annotation_name: annotation.annotation_name)
137
+ ).annotation_payload
138
+ }
139
+ end
140
+ end
141
+
55
142
  def initiate_upload(options)
56
143
  options = options_for(:create_multipart_upload, options)
57
144
  @client.create_multipart_upload(options).upload_id
@@ -63,28 +150,30 @@ module Aws
63
150
  @thread_count.times do
64
151
  threads << copy_part_thread(queue)
65
152
  end
66
- threads.map(&:value).flatten.sort_by{ |part| part[:part_number] }
153
+ threads.map(&:value).flatten.sort_by { |part| part[:part_number] }
67
154
  end
68
155
 
69
- def copy_part_thread(queue)
156
+ def copy_part_thread(queue) # rubocop:disable Metrics/MethodLength
70
157
  Thread.new do
71
- begin
158
+ begin # rubocop:disable Style/RedundantBegin
72
159
  completed = []
73
- while part = queue.shift
160
+ while (part = queue.shift)
74
161
  completed << copy_part(part)
75
162
  end
76
163
  completed
77
- rescue => error
164
+ rescue StandardError => e
78
165
  queue.clear!
79
- raise error
166
+ raise e
80
167
  end
81
168
  end
82
169
  end
83
170
 
84
171
  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) }
172
+ @client.upload_part_copy(part)
173
+ .copy_part_result
174
+ .to_h
175
+ .merge({ part_number: part[:part_number] })
176
+ .tap { |result| result.delete(:last_modified) }
88
177
  end
89
178
 
90
179
  def complete_upload(parts, options)
@@ -94,100 +183,104 @@ module Aws
94
183
  end
95
184
 
96
185
  def abort_upload(options)
97
- @client.abort_multipart_upload({
98
- bucket: options[:bucket],
99
- key: options[:key],
100
- upload_id: options[:upload_id],
101
- })
186
+ @client.abort_multipart_upload(
187
+ {
188
+ bucket: options[:bucket],
189
+ key: options[:key],
190
+ upload_id: options[:upload_id]
191
+ }
192
+ )
102
193
  end
103
194
 
104
- def compute_parts(size, default_part_size, options)
195
+ def compute_parts(size, default_part_size, options) # rubocop:disable Metrics/MethodLength
105
196
  part_number = 1
106
197
  offset = 0
107
198
  parts = []
108
199
  options = options_for(:upload_part_copy, options)
200
+ options[:copy_source_if_match] = @source_etag if @source_etag
109
201
  while offset < size
110
202
  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
- })
203
+ parts << options.merge({ part_number: part_number, copy_source_range: byte_range(offset, part_size, size) })
115
204
  part_number += 1
116
205
  offset += part_size
117
206
  end
118
207
  parts
119
208
  end
120
209
 
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]
210
+ def calculate_part_size(part_number, default_part_size, _options)
211
+ if @source_parts_count
212
+ resolve_part_size(part_number)
132
213
  else
133
214
  default_part_size
134
215
  end
135
216
  end
136
217
 
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]
218
+ def resolve_part_size(part_number)
219
+ return @first_part_size if part_number == 1 && @first_part_size
143
220
 
144
- client = options[:copy_source_client] || @client
221
+ @source_client.head_object(@source.merge(part_number: part_number)).content_length
222
+ end
145
223
 
146
- if vid_match = options[:copy_source].match(/([^\/]+?)\/(.+)\?versionId=(.+)/)
147
- bucket, key, version_id = vid_match[1,3]
224
+ def byte_range(offset, part_size, size)
225
+ if offset + part_size < size
226
+ "bytes=#{offset}-#{offset + part_size - 1}"
148
227
  else
149
- bucket, key = options[:copy_source].match(/([^\/]+?)\/(.+)/)[1,2]
228
+ "bytes=#{offset}-#{size - 1}"
150
229
  end
230
+ end
151
231
 
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
- }
232
+ def default_part_size(source_size)
233
+ raise ArgumentError, 'unable to multipart copy files smaller than 5MB' if source_size < MIN_PART_SIZE
156
234
 
157
- client.head_object(head_opts).to_h.tap { |head|
158
- head.delete(:server_side_encryption)
159
- head.delete(:ssekms_key_id)
160
- }
235
+ [(source_size.to_f / MAX_PARTS).ceil, @min_part_size].max.to_i
161
236
  end
162
237
 
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
238
+ def put_tags(tags, resp, options)
239
+ @client.put_object_tagging(
240
+ bucket: options[:bucket],
241
+ key: options[:key],
242
+ tagging: { tag_set: tags },
243
+ version_id: resp.version_id
244
+ )
245
+ rescue StandardError => e
246
+ raise StandardError,
247
+ 'Object data copied successfully. ' \
248
+ "Failed to copy tags: #{e.message}"
249
+ end
250
+
251
+ def put_annotations(annotations, response, options) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
252
+ succeeded = []
253
+ failed = []
254
+
255
+ annotations.each do |annotation|
256
+ @client.put_object_annotation(
257
+ bucket: options[:bucket],
258
+ key: options[:key],
259
+ version_id: response.version_id,
260
+ object_if_match: response.etag,
261
+ annotation_name: annotation[:name],
262
+ annotation_payload: annotation[:payload]
263
+ )
264
+ succeeded << annotation[:name]
265
+ rescue StandardError => e
266
+ failed << { key: annotation[:name], error: e }
168
267
  end
268
+ return if failed.empty?
269
+
270
+ raise StandardError,
271
+ 'Object data copied successfully. Failed to copy annotations: ' \
272
+ "#{failed.map { |f| "#{f[:key]} (#{f[:error].message})" }.join(', ')}. " \
273
+ "Succeeded: #{succeeded.join(', ')}"
169
274
  end
170
275
 
171
276
  def options_for(operation_name, options)
172
- API_OPTIONS[operation_name].inject({}) do |hash, opt_name|
277
+ API_OPTIONS[operation_name].each_with_object({}) do |opt_name, hash|
173
278
  hash[opt_name] = options[opt_name] if options.key?(opt_name)
174
- hash
175
279
  end
176
280
  end
177
281
 
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
-
282
+ # A thread-safe work queue of part definitions for a multipart copy.
189
283
  class PartQueue
190
-
191
284
  def initialize(parts = [])
192
285
  @parts = parts
193
286
  @mutex = Mutex.new
@@ -200,7 +293,6 @@ module Aws
200
293
  def clear!
201
294
  @mutex.synchronize { @parts.clear }
202
295
  end
203
-
204
296
  end
205
297
  end
206
298
  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