aws-sdk 1.17.0 → 1.18.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.
- checksums.yaml +4 -4
- data/lib/aws/api_config/{EC2-2013-07-15.yml → EC2-2013-08-15.yml} +76 -1
- data/lib/aws/api_config/OpsWorks-2013-02-18.yml +24 -0
- data/lib/aws/core/client.rb +6 -2
- data/lib/aws/ec2/client.rb +3 -3
- data/lib/aws/glacier/vault.rb +2 -2
- data/lib/aws/s3/client.rb +1026 -1025
- data/lib/aws/simple_workflow.rb +1 -1
- data/lib/aws/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 922eab0a8bd13d682f4ac39b61b26540803ba0bb
|
4
|
+
data.tar.gz: f4cbc9198f4fbd9b353e5869a140ecc6e5c76703
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab1afa3d7bcace527f0d20a8c9cb769a4bc36dab589138f4dcf692fa86d014db312d15398e1c051ca150cb1927b1c3acabf15a7f350784f93ff55e06af7781d9
|
7
|
+
data.tar.gz: b74beeb8fbd0d50c9a4e1f6870a571e047a50f5d4e7b1d7000ab6ed4cd1a68ca9d9cee405543d510e8dc7715af544674bede3cd9120bdb1db531a94067cbc3c2
|
@@ -12,7 +12,7 @@
|
|
12
12
|
# language governing permissions and limitations under the License.
|
13
13
|
|
14
14
|
---
|
15
|
-
:api_version: '2013-
|
15
|
+
:api_version: '2013-08-15'
|
16
16
|
:operations:
|
17
17
|
- :name: ActivateLicense
|
18
18
|
:method: :activate_license
|
@@ -2337,6 +2337,8 @@
|
|
2337
2337
|
:children:
|
2338
2338
|
start:
|
2339
2339
|
:type: :time
|
2340
|
+
end:
|
2341
|
+
:type: :time
|
2340
2342
|
duration:
|
2341
2343
|
:type: :integer
|
2342
2344
|
usagePrice:
|
@@ -2417,6 +2419,57 @@
|
|
2417
2419
|
item:
|
2418
2420
|
:rename: :tag_set
|
2419
2421
|
:list: true
|
2422
|
+
- :name: DescribeReservedInstancesModifications
|
2423
|
+
:method: :describe_reserved_instances_modifications
|
2424
|
+
:inputs:
|
2425
|
+
ReservedInstancesModificationId:
|
2426
|
+
- :list:
|
2427
|
+
- :string
|
2428
|
+
- :rename: ReservedInstancesModificationIds
|
2429
|
+
NextToken:
|
2430
|
+
- :string
|
2431
|
+
Filter:
|
2432
|
+
- :list:
|
2433
|
+
- :structure:
|
2434
|
+
Name:
|
2435
|
+
- :string
|
2436
|
+
Value:
|
2437
|
+
- :list:
|
2438
|
+
- :string
|
2439
|
+
- :rename: Values
|
2440
|
+
- :rename: filters
|
2441
|
+
:outputs:
|
2442
|
+
:children:
|
2443
|
+
reservedInstancesModificationsSet:
|
2444
|
+
:ignore: true
|
2445
|
+
:children:
|
2446
|
+
item:
|
2447
|
+
:rename: :reserved_instances_modifications_set
|
2448
|
+
:list: true
|
2449
|
+
:children:
|
2450
|
+
reservedInstancesSet:
|
2451
|
+
:ignore: true
|
2452
|
+
:children:
|
2453
|
+
item:
|
2454
|
+
:rename: :reserved_instances_set
|
2455
|
+
:list: true
|
2456
|
+
modificationResultSet:
|
2457
|
+
:ignore: true
|
2458
|
+
:children:
|
2459
|
+
item:
|
2460
|
+
:rename: :modification_result_set
|
2461
|
+
:list: true
|
2462
|
+
:children:
|
2463
|
+
targetConfiguration:
|
2464
|
+
:children:
|
2465
|
+
instanceCount:
|
2466
|
+
:type: :integer
|
2467
|
+
createDate:
|
2468
|
+
:type: :time
|
2469
|
+
updateDate:
|
2470
|
+
:type: :time
|
2471
|
+
effectiveDate:
|
2472
|
+
:type: :time
|
2420
2473
|
- :name: DescribeReservedInstancesOfferings
|
2421
2474
|
:method: :describe_reserved_instances_offerings
|
2422
2475
|
:inputs:
|
@@ -3671,6 +3724,28 @@
|
|
3671
3724
|
DeleteOnTermination:
|
3672
3725
|
- :boolean
|
3673
3726
|
:outputs: {}
|
3727
|
+
- :name: ModifyReservedInstances
|
3728
|
+
:method: :modify_reserved_instances
|
3729
|
+
:inputs:
|
3730
|
+
ClientToken:
|
3731
|
+
- :string
|
3732
|
+
ReservedInstancesId:
|
3733
|
+
- :list:
|
3734
|
+
- :string
|
3735
|
+
- :required
|
3736
|
+
- :rename: ReservedInstancesIds
|
3737
|
+
ReservedInstancesConfigurationSetItemType:
|
3738
|
+
- :list:
|
3739
|
+
- :structure:
|
3740
|
+
AvailabilityZone:
|
3741
|
+
- :string
|
3742
|
+
Platform:
|
3743
|
+
- :string
|
3744
|
+
InstanceCount:
|
3745
|
+
- :integer
|
3746
|
+
- :required
|
3747
|
+
- :rename: targetConfigurations
|
3748
|
+
:outputs: {}
|
3674
3749
|
- :name: ModifySnapshotAttribute
|
3675
3750
|
:method: :modify_snapshot_attribute
|
3676
3751
|
:inputs:
|
@@ -36,6 +36,8 @@
|
|
36
36
|
- :string
|
37
37
|
Region:
|
38
38
|
- :string
|
39
|
+
VpcId:
|
40
|
+
- :string
|
39
41
|
Attributes:
|
40
42
|
- :map:
|
41
43
|
:key:
|
@@ -53,6 +55,8 @@
|
|
53
55
|
- :string
|
54
56
|
DefaultAvailabilityZone:
|
55
57
|
- :string
|
58
|
+
DefaultSubnetId:
|
59
|
+
- :string
|
56
60
|
CustomJson:
|
57
61
|
- :string
|
58
62
|
ConfigurationManager:
|
@@ -202,6 +206,8 @@
|
|
202
206
|
- :string
|
203
207
|
AvailabilityZone:
|
204
208
|
- :string
|
209
|
+
SubnetId:
|
210
|
+
- :string
|
205
211
|
Architecture:
|
206
212
|
- :string
|
207
213
|
RootDeviceType:
|
@@ -291,6 +297,8 @@
|
|
291
297
|
Region:
|
292
298
|
- :string
|
293
299
|
- :required
|
300
|
+
VpcId:
|
301
|
+
- :string
|
294
302
|
Attributes:
|
295
303
|
- :map:
|
296
304
|
:key:
|
@@ -309,6 +317,8 @@
|
|
309
317
|
- :string
|
310
318
|
DefaultAvailabilityZone:
|
311
319
|
- :string
|
320
|
+
DefaultSubnetId:
|
321
|
+
- :string
|
312
322
|
CustomJson:
|
313
323
|
- :string
|
314
324
|
ConfigurationManager:
|
@@ -640,6 +650,9 @@
|
|
640
650
|
AvailabilityZones:
|
641
651
|
:sym: :availability_zones
|
642
652
|
:type: :string
|
653
|
+
SubnetIds:
|
654
|
+
:sym: :subnet_ids
|
655
|
+
:type: :string
|
643
656
|
Ec2InstanceIds:
|
644
657
|
:sym: :ec_2_instance_ids
|
645
658
|
:type: :string
|
@@ -694,6 +707,9 @@
|
|
694
707
|
AvailabilityZone:
|
695
708
|
:sym: :availability_zone
|
696
709
|
:type: :string
|
710
|
+
SubnetId:
|
711
|
+
:sym: :subnet_id
|
712
|
+
:type: :string
|
697
713
|
PublicDns:
|
698
714
|
:sym: :public_dns
|
699
715
|
:type: :string
|
@@ -1036,6 +1052,9 @@
|
|
1036
1052
|
Region:
|
1037
1053
|
:sym: :region
|
1038
1054
|
:type: :string
|
1055
|
+
VpcId:
|
1056
|
+
:sym: :vpc_id
|
1057
|
+
:type: :string
|
1039
1058
|
Attributes:
|
1040
1059
|
:sym: :attributes
|
1041
1060
|
:type: :map
|
@@ -1057,6 +1076,9 @@
|
|
1057
1076
|
DefaultAvailabilityZone:
|
1058
1077
|
:sym: :default_availability_zone
|
1059
1078
|
:type: :string
|
1079
|
+
DefaultSubnetId:
|
1080
|
+
:sym: :default_subnet_id
|
1081
|
+
:type: :string
|
1060
1082
|
CustomJson:
|
1061
1083
|
:sym: :custom_json
|
1062
1084
|
:type: :string
|
@@ -1557,6 +1579,8 @@
|
|
1557
1579
|
- :string
|
1558
1580
|
DefaultAvailabilityZone:
|
1559
1581
|
- :string
|
1582
|
+
DefaultSubnetId:
|
1583
|
+
- :string
|
1560
1584
|
CustomJson:
|
1561
1585
|
- :string
|
1562
1586
|
ConfigurationManager:
|
data/lib/aws/core/client.rb
CHANGED
@@ -581,8 +581,12 @@ module AWS
|
|
581
581
|
# @return [Array<Symbol>] Returns a list of service operations as
|
582
582
|
# method names supported by this client.
|
583
583
|
# @api private
|
584
|
-
def operations
|
585
|
-
|
584
|
+
def operations(options = {})
|
585
|
+
if name.match(/V\d{8}$/)
|
586
|
+
@operations ||= []
|
587
|
+
else
|
588
|
+
client_class(options).operations
|
589
|
+
end
|
586
590
|
end
|
587
591
|
|
588
592
|
# @api private
|
data/lib/aws/ec2/client.rb
CHANGED
@@ -17,7 +17,7 @@ module AWS
|
|
17
17
|
# Client class for Amazon Elastic Compute Cloud (EC2).
|
18
18
|
class Client < Core::QueryClient
|
19
19
|
|
20
|
-
API_VERSION = '2013-
|
20
|
+
API_VERSION = '2013-08-15'
|
21
21
|
|
22
22
|
# @api private
|
23
23
|
CACHEABLE_REQUESTS = Set[
|
@@ -60,9 +60,9 @@ module AWS
|
|
60
60
|
|
61
61
|
end
|
62
62
|
|
63
|
-
class Client::
|
63
|
+
class Client::V20130815 < Client
|
64
64
|
|
65
|
-
define_client_methods('2013-
|
65
|
+
define_client_methods('2013-08-15')
|
66
66
|
|
67
67
|
end
|
68
68
|
|
data/lib/aws/glacier/vault.rb
CHANGED
@@ -50,11 +50,11 @@ module AWS
|
|
50
50
|
|
51
51
|
populates_from(:list_vaults) do |resp|
|
52
52
|
resp.request_options[:account_id] == account_id and
|
53
|
-
resp[:vault_list].find {|vault| vault[:
|
53
|
+
resp[:vault_list].find {|vault| vault[:vault_name] == name }
|
54
54
|
end
|
55
55
|
|
56
56
|
populates_from(:describe_vault) do |resp|
|
57
|
-
if resp.request_options[:account_id] == account_id and resp[:
|
57
|
+
if resp.request_options[:account_id] == account_id and resp[:vault_name] == name
|
58
58
|
resp
|
59
59
|
end
|
60
60
|
end
|
data/lib/aws/s3/client.rb
CHANGED
@@ -99,678 +99,943 @@ module AWS
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
-
|
103
|
-
bucket_method(method_name, verb, *args) do
|
104
|
-
configure_request do |req, options|
|
105
|
-
validate_key!(options[:key])
|
106
|
-
super(req, options)
|
107
|
-
req.key = options[:key]
|
108
|
-
end
|
102
|
+
protected
|
109
103
|
|
110
|
-
|
104
|
+
def extract_error_details response
|
105
|
+
if
|
106
|
+
(response.http_response.status >= 300 ||
|
107
|
+
response.request_type == :complete_multipart_upload) and
|
108
|
+
body = response.http_response.body and
|
109
|
+
error = Core::XML::Parser.parse(body) and
|
110
|
+
error[:code]
|
111
|
+
then
|
112
|
+
[error[:code], error[:message]]
|
111
113
|
end
|
112
114
|
end
|
113
115
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
# @overload create_bucket(options = {})
|
118
|
-
# @param [Hash] options
|
119
|
-
# @option options [required,String] :bucket_name
|
120
|
-
# @option options [String] :acl A canned ACL (e.g. 'private',
|
121
|
-
# 'public-read', etc). See the S3 API documentation for
|
122
|
-
# a complete list of valid values.
|
123
|
-
# @option options [String] :grant_read
|
124
|
-
# @option options [String] :grant_write
|
125
|
-
# @option options [String] :grant_read_acp
|
126
|
-
# @option options [String] :grant_write_acp
|
127
|
-
# @option options [String] :grant_full_control
|
128
|
-
# @return [Core::Response]
|
129
|
-
bucket_method(:create_bucket, :put, :header_options => {
|
130
|
-
:acl => 'x-amz-acl',
|
131
|
-
:grant_read => 'x-amz-grant-read',
|
132
|
-
:grant_write => 'x-amz-grant-write',
|
133
|
-
:grant_read_acp => 'x-amz-grant-read-acp',
|
134
|
-
:grant_write_acp => 'x-amz-grant-write-acp',
|
135
|
-
:grant_full_control => 'x-amz-grant-full-control',
|
136
|
-
}) do
|
116
|
+
def empty_response_body? response_body
|
117
|
+
response_body.nil? or response_body == ''
|
118
|
+
end
|
137
119
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
super(req, options)
|
120
|
+
# There are a few of s3 requests that can generate empty bodies and
|
121
|
+
# yet still be errors. These return empty bodies to comply with the
|
122
|
+
# HTTP spec. We have to detect these errors specially.
|
123
|
+
def populate_error resp
|
124
|
+
code = resp.http_response.status
|
125
|
+
if EMPTY_BODY_ERRORS.include?(code) and empty_response_body?(resp.http_response.body)
|
126
|
+
error_class = EMPTY_BODY_ERRORS[code]
|
127
|
+
resp.error = error_class.new(resp.http_request, resp.http_response)
|
128
|
+
else
|
129
|
+
super
|
149
130
|
end
|
150
|
-
|
151
131
|
end
|
152
|
-
alias_method :put_bucket, :create_bucket
|
153
|
-
|
154
|
-
# @!method put_bucket_website(options = {})
|
155
|
-
# @param [Hash] options
|
156
|
-
# @option (see WebsiteConfiguration#initialize)
|
157
|
-
# @option options [required,String] :bucket_name
|
158
|
-
# @return [Core::Response]
|
159
|
-
bucket_method(:put_bucket_website, :put, 'website') do
|
160
132
|
|
161
|
-
|
162
|
-
|
163
|
-
|
133
|
+
def retryable_error? response
|
134
|
+
super or
|
135
|
+
failed_multipart_upload?(response) or
|
136
|
+
response.error.is_a?(Errors::RequestTimeout)
|
137
|
+
end
|
164
138
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
139
|
+
# S3 may return a 200 response code in response to complete_multipart_upload
|
140
|
+
# and then start streaming whitespace until it knows the final result.
|
141
|
+
# At that time it sends an XML message with success or failure.
|
142
|
+
def failed_multipart_upload? response
|
143
|
+
response.request_type == :complete_multipart_upload &&
|
144
|
+
extract_error_details(response)
|
145
|
+
end
|
171
146
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
147
|
+
def new_request
|
148
|
+
req = S3::Request.new
|
149
|
+
req.force_path_style = config.s3_force_path_style?
|
150
|
+
req
|
151
|
+
end
|
177
152
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
153
|
+
# Previously the access control policy could be specified via :acl
|
154
|
+
# as a string or an object that responds to #to_xml. The prefered
|
155
|
+
# method now is to pass :access_control_policy an xml document.
|
156
|
+
def move_access_control_policy options
|
157
|
+
if acl = options[:acl]
|
158
|
+
if acl.is_a?(String) and is_xml?(acl)
|
159
|
+
options[:access_control_policy] = options.delete(:acl)
|
160
|
+
elsif acl.respond_to?(:to_xml)
|
161
|
+
options[:access_control_policy] = options.delete(:acl).to_xml
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
183
165
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
166
|
+
# @param [String] possible_xml
|
167
|
+
# @return [Boolean] Returns `true` if the given string is a valid xml
|
168
|
+
# document.
|
169
|
+
def is_xml? possible_xml
|
170
|
+
begin
|
171
|
+
REXML::Document.new(possible_xml).has_elements?
|
172
|
+
rescue
|
173
|
+
false
|
174
|
+
end
|
175
|
+
end
|
189
176
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
xml.HostName(redirect[:host_name]) if redirect[:host_name]
|
194
|
-
xml.ReplaceKeyPrefixWith(redirect[:replace_key_prefix_with]) if redirect[:replace_key_prefix_with]
|
195
|
-
xml.ReplaceKeyWith(redirect[:replace_key_with]) if redirect[:replace_key_with]
|
196
|
-
xml.HttpRedirectCode(redirect[:http_redirect_code]) if redirect[:http_redirect_code]
|
197
|
-
end
|
177
|
+
def md5 str
|
178
|
+
Base64.encode64(Digest::MD5.digest(str)).strip
|
179
|
+
end
|
198
180
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
181
|
+
def parse_copy_part_response resp
|
182
|
+
doc = REXML::Document.new(resp.http_response.body)
|
183
|
+
resp[:etag] = doc.root.elements["ETag"].text
|
184
|
+
resp[:last_modified] = doc.root.elements["LastModified"].text
|
185
|
+
if header = resp.http_response.headers['x-amzn-requestid']
|
186
|
+
data[:request_id] = [header].flatten.first
|
187
|
+
end
|
188
|
+
end
|
205
189
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
190
|
+
def extract_object_headers resp
|
191
|
+
meta = {}
|
192
|
+
resp.http_response.headers.each_pair do |name,value|
|
193
|
+
if name =~ /^x-amz-meta-(.+)$/i
|
194
|
+
meta[$1] = [value].flatten.join
|
195
|
+
end
|
196
|
+
end
|
197
|
+
resp.data[:meta] = meta
|
210
198
|
|
211
|
-
|
212
|
-
|
213
|
-
|
199
|
+
if expiry = resp.http_response.headers['x-amz-expiration']
|
200
|
+
expiry.first =~ /^expiry-date="(.+)", rule-id="(.+)"$/
|
201
|
+
exp_date = DateTime.parse($1)
|
202
|
+
exp_rule_id = $2
|
203
|
+
else
|
204
|
+
exp_date = nil
|
205
|
+
exp_rule_id = nil
|
214
206
|
end
|
207
|
+
resp.data[:expiration_date] = exp_date if exp_date
|
208
|
+
resp.data[:expiration_rule_id] = exp_rule_id if exp_rule_id
|
215
209
|
|
216
|
-
|
210
|
+
restoring = false
|
211
|
+
restore_date = nil
|
217
212
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
213
|
+
if restore = resp.http_response.headers['x-amz-restore']
|
214
|
+
if restore.first =~ /ongoing-request="(.+?)", expiry-date="(.+?)"/
|
215
|
+
restoring = $1 == "true"
|
216
|
+
restore_date = $2 && DateTime.parse($2)
|
217
|
+
elsif restore.first =~ /ongoing-request="(.+?)"/
|
218
|
+
restoring = $1 == "true"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
resp.data[:restore_in_progress] = restoring
|
222
|
+
resp.data[:restore_expiration_date] = restore_date if restore_date
|
227
223
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
224
|
+
{
|
225
|
+
'x-amz-version-id' => :version_id,
|
226
|
+
'content-type' => :content_type,
|
227
|
+
'content-encoding' => :content_encoding,
|
228
|
+
'cache-control' => :cache_control,
|
229
|
+
'expires' => :expires,
|
230
|
+
'etag' => :etag,
|
231
|
+
'x-amz-website-redirect-location' => :website_redirect_location,
|
232
|
+
'accept-ranges' => :accept_ranges,
|
233
|
+
}.each_pair do |header,method|
|
234
|
+
if value = resp.http_response.header(header)
|
235
|
+
resp.data[method] = value
|
236
|
+
end
|
237
|
+
end
|
233
238
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
# @option options [required,String] :bucket_name
|
238
|
-
# @return [Core::Response]
|
239
|
-
bucket_method(:delete_bucket, :delete)
|
239
|
+
if time = resp.http_response.header('Last-Modified')
|
240
|
+
resp.data[:last_modified] = Time.parse(time)
|
241
|
+
end
|
240
242
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
# @option options [required,String] :lifecycle_configuration
|
245
|
-
# @return [Core::Response]
|
246
|
-
bucket_method(:set_bucket_lifecycle_configuration, :put) do
|
243
|
+
if length = resp.http_response.header('content-length')
|
244
|
+
resp.data[:content_length] = length.to_i
|
245
|
+
end
|
247
246
|
|
248
|
-
|
249
|
-
|
250
|
-
req.add_param('lifecycle')
|
251
|
-
req.body = xml
|
252
|
-
req.headers['content-md5'] = md5(xml)
|
253
|
-
super(req, options)
|
247
|
+
if sse = resp.http_response.header('x-amz-server-side-encryption')
|
248
|
+
resp.data[:server_side_encryption] = sse.downcase.to_sym
|
254
249
|
end
|
255
250
|
|
256
251
|
end
|
257
252
|
|
258
|
-
|
259
|
-
# @param [Hash] options
|
260
|
-
# @option options [required,String] :bucket_name
|
261
|
-
# @return [Core::Response]
|
262
|
-
bucket_method(:get_bucket_lifecycle_configuration, :get) do
|
263
|
-
|
264
|
-
configure_request do |req, options|
|
265
|
-
req.add_param('lifecycle')
|
266
|
-
super(req, options)
|
267
|
-
end
|
268
|
-
|
269
|
-
process_response do |resp|
|
270
|
-
xml = resp.http_response.body
|
271
|
-
resp.data = XML::GetBucketLifecycleConfiguration.parse(xml)
|
272
|
-
end
|
273
|
-
|
274
|
-
end
|
275
|
-
|
276
|
-
# @overload delete_bucket_lifecycle_configuration(options = {})
|
277
|
-
# @param [Hash] options
|
278
|
-
# @option options [required,String] :bucket_name
|
279
|
-
# @return [Core::Response]
|
280
|
-
bucket_method(:delete_bucket_lifecycle_configuration, :delete) do
|
253
|
+
module Validators
|
281
254
|
|
282
|
-
|
283
|
-
|
284
|
-
|
255
|
+
# @return [Boolean] Returns true if the given bucket name is valid.
|
256
|
+
def valid_bucket_name?(bucket_name)
|
257
|
+
validate_bucket_name!(bucket_name) rescue false
|
285
258
|
end
|
286
259
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
# specified in the Access-Control-Request-Headers header must
|
306
|
-
# have a corresponding entry in the rule.
|
307
|
-
# Amazon S3 will send only the allowed headers in a response
|
308
|
-
# that were requested. This can contain at most one * wild
|
309
|
-
# character.
|
310
|
-
# * `:max_age_seconds` - (Integer) The time in seconds that your
|
311
|
-
# browser is to cache the preflight response for the specified
|
312
|
-
# resource.
|
313
|
-
# * `:expose_headers` - (Array<String>) One or more headers in
|
314
|
-
# the response that you want customers to be able to access
|
315
|
-
# from their applications (for example, from a JavaScript
|
316
|
-
# XMLHttpRequest object).
|
317
|
-
# @return [Core::Response]
|
318
|
-
bucket_method(:put_bucket_cors, :put) do
|
319
|
-
configure_request do |req, options|
|
260
|
+
# Returns true if the given `bucket_name` is DNS compatible.
|
261
|
+
#
|
262
|
+
# DNS compatible bucket names may be accessed like:
|
263
|
+
#
|
264
|
+
# http://dns.compat.bucket.name.s3.amazonaws.com/
|
265
|
+
#
|
266
|
+
# Whereas non-dns compatible bucket names must place the bucket
|
267
|
+
# name in the url path, like:
|
268
|
+
#
|
269
|
+
# http://s3.amazonaws.com/dns_incompat_bucket_name/
|
270
|
+
#
|
271
|
+
# @return [Boolean] Returns true if the given bucket name may be
|
272
|
+
# is dns compatible.
|
273
|
+
# this bucket n
|
274
|
+
#
|
275
|
+
def dns_compatible_bucket_name?(bucket_name)
|
276
|
+
return false if
|
277
|
+
!valid_bucket_name?(bucket_name) or
|
320
278
|
|
321
|
-
|
279
|
+
# Bucket names should be between 3 and 63 characters long
|
280
|
+
bucket_name.size > 63 or
|
322
281
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
xml.CORSRule do
|
282
|
+
# Bucket names must only contain lowercase letters, numbers, dots, and dashes
|
283
|
+
# and must start and end with a lowercase letter or a number
|
284
|
+
bucket_name !~ /^[a-z0-9][a-z0-9.-]+[a-z0-9]$/ or
|
327
285
|
|
328
|
-
|
286
|
+
# Bucket names should not be formatted like an IP address (e.g., 192.168.5.4)
|
287
|
+
bucket_name =~ /(\d+\.){3}\d+/ or
|
329
288
|
|
330
|
-
|
331
|
-
|
332
|
-
end
|
289
|
+
# Bucket names cannot contain two, adjacent periods
|
290
|
+
bucket_name['..'] or
|
333
291
|
|
334
|
-
|
335
|
-
|
336
|
-
|
292
|
+
# Bucket names cannot contain dashes next to periods
|
293
|
+
# (e.g., "my-.bucket.com" and "my.-bucket" are invalid)
|
294
|
+
(bucket_name['-.'] || bucket_name['.-'])
|
337
295
|
|
338
|
-
|
339
|
-
|
340
|
-
end
|
296
|
+
true
|
297
|
+
end
|
341
298
|
|
342
|
-
|
343
|
-
|
299
|
+
# Returns true if the bucket name must be used in the request
|
300
|
+
# path instead of as a sub-domain when making requests against
|
301
|
+
# S3.
|
302
|
+
#
|
303
|
+
# This can be an issue if the bucket name is DNS compatible but
|
304
|
+
# contains '.' (periods). These cause the SSL certificate to
|
305
|
+
# become invalid when making authenticated requets over SSL to the
|
306
|
+
# bucket name. The solution is to send this as a path argument
|
307
|
+
# instead.
|
308
|
+
#
|
309
|
+
# @return [Boolean] Returns true if the bucket name should be used
|
310
|
+
# as a path segement instead of dns prefix when making requests
|
311
|
+
# against s3.
|
312
|
+
#
|
313
|
+
def path_style_bucket_name? bucket_name
|
314
|
+
if dns_compatible_bucket_name?(bucket_name)
|
315
|
+
bucket_name =~ /\./ ? true : false
|
316
|
+
else
|
317
|
+
true
|
318
|
+
end
|
319
|
+
end
|
344
320
|
|
345
|
-
|
346
|
-
|
347
|
-
|
321
|
+
def validate! name, value, &block
|
322
|
+
if error_msg = yield
|
323
|
+
raise ArgumentError, "#{name} #{error_msg}"
|
324
|
+
end
|
325
|
+
value
|
326
|
+
end
|
348
327
|
|
349
|
-
|
350
|
-
|
328
|
+
def validate_key!(key)
|
329
|
+
validate!('key', key) do
|
330
|
+
case
|
331
|
+
when key.nil? || key == ''
|
332
|
+
'may not be blank'
|
351
333
|
end
|
352
|
-
end
|
353
|
-
|
354
|
-
req.body = xml
|
355
|
-
req.headers['content-md5'] = md5(xml)
|
356
|
-
|
357
|
-
super(req, options)
|
358
|
-
|
334
|
+
end
|
359
335
|
end
|
360
|
-
end
|
361
|
-
|
362
|
-
# @overload get_bucket_cors(options = {})
|
363
|
-
# @param [Hash] options
|
364
|
-
# @option options [required,String] :bucket_name
|
365
|
-
# @return [Core::Response]
|
366
|
-
bucket_method(:get_bucket_cors, :get) do
|
367
336
|
|
368
|
-
|
369
|
-
|
370
|
-
|
337
|
+
def require_bucket_name! bucket_name
|
338
|
+
if [nil, ''].include?(bucket_name)
|
339
|
+
raise ArgumentError, "bucket_name may not be blank"
|
340
|
+
end
|
371
341
|
end
|
372
342
|
|
373
|
-
|
374
|
-
|
343
|
+
# Returns true if the given bucket name is valid. If the name
|
344
|
+
# is invalid, an ArgumentError is raised.
|
345
|
+
def validate_bucket_name!(bucket_name)
|
346
|
+
validate!('bucket_name', bucket_name) do
|
347
|
+
case
|
348
|
+
when bucket_name.nil? || bucket_name == ''
|
349
|
+
'may not be blank'
|
350
|
+
when bucket_name !~ /^[A-Za-z0-9._\-]+$/
|
351
|
+
'may only contain uppercase letters, lowercase letters, numbers, periods (.), ' +
|
352
|
+
'underscores (_), and dashes (-)'
|
353
|
+
when !(3..255).include?(bucket_name.size)
|
354
|
+
'must be between 3 and 255 characters long'
|
355
|
+
when bucket_name =~ /\n/
|
356
|
+
'must not contain a newline character'
|
357
|
+
end
|
358
|
+
end
|
375
359
|
end
|
376
360
|
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
super(req, options)
|
361
|
+
def require_policy!(policy)
|
362
|
+
validate!('policy', policy) do
|
363
|
+
case
|
364
|
+
when policy.nil? || policy == ''
|
365
|
+
'may not be blank'
|
366
|
+
else
|
367
|
+
json_validation_message(policy)
|
368
|
+
end
|
369
|
+
end
|
387
370
|
end
|
388
|
-
end
|
389
371
|
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
372
|
+
def require_acl! options
|
373
|
+
acl_options = [
|
374
|
+
:acl,
|
375
|
+
:grant_read,
|
376
|
+
:grant_write,
|
377
|
+
:grant_read_acp,
|
378
|
+
:grant_write_acp,
|
379
|
+
:grant_full_control,
|
380
|
+
:access_control_policy,
|
381
|
+
]
|
382
|
+
unless options.keys.any?{|opt| acl_options.include?(opt) }
|
383
|
+
msg = "missing a required ACL option, must provide an ACL " +
|
384
|
+
"via :acl, :grant_* or :access_control_policy"
|
385
|
+
raise ArgumentError, msg
|
386
|
+
end
|
387
|
+
end
|
397
388
|
|
398
|
-
|
389
|
+
def set_body_stream_and_content_length request, options
|
399
390
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
xml.Key(key)
|
406
|
-
xml.Value(value)
|
407
|
-
end
|
408
|
-
end
|
409
|
-
end
|
391
|
+
unless options[:content_length]
|
392
|
+
msg = "S3 requires a content-length header, unable to determine "
|
393
|
+
msg << "the content length of the data provided, please set "
|
394
|
+
msg << ":content_length"
|
395
|
+
raise ArgumentError, msg
|
410
396
|
end
|
411
397
|
|
412
|
-
|
413
|
-
|
414
|
-
req.headers['content-md5'] = md5(xml)
|
415
|
-
|
416
|
-
super(req, options)
|
398
|
+
request.headers['content-length'] = options[:content_length]
|
399
|
+
request.body_stream = options[:data]
|
417
400
|
|
418
401
|
end
|
419
|
-
end
|
420
402
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
403
|
+
def require_upload_id!(upload_id)
|
404
|
+
validate!("upload_id", upload_id) do
|
405
|
+
"must not be blank" if upload_id.to_s.empty?
|
406
|
+
end
|
407
|
+
end
|
426
408
|
|
427
|
-
|
428
|
-
|
429
|
-
|
409
|
+
def require_part_number! part_number
|
410
|
+
validate!("part_number", part_number) do
|
411
|
+
"must not be blank" if part_number.to_s.empty?
|
412
|
+
end
|
430
413
|
end
|
431
414
|
|
432
|
-
|
433
|
-
|
415
|
+
def validate_parts!(parts)
|
416
|
+
validate!("parts", parts) do
|
417
|
+
if !parts.kind_of?(Array)
|
418
|
+
"must not be blank"
|
419
|
+
elsif parts.empty?
|
420
|
+
"must contain at least one entry"
|
421
|
+
elsif !parts.all? { |p| p.kind_of?(Hash) }
|
422
|
+
"must be an array of hashes"
|
423
|
+
elsif !parts.all? { |p| p[:part_number] }
|
424
|
+
"must contain part_number for each part"
|
425
|
+
elsif !parts.all? { |p| p[:etag] }
|
426
|
+
"must contain etag for each part"
|
427
|
+
elsif parts.any? { |p| p[:part_number].to_i < 1 }
|
428
|
+
"must not have part numbers less than 1"
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
def json_validation_message(obj)
|
434
|
+
if obj.respond_to?(:to_str)
|
435
|
+
obj = obj.to_str
|
436
|
+
elsif obj.respond_to?(:to_json)
|
437
|
+
obj = obj.to_json
|
438
|
+
end
|
439
|
+
|
440
|
+
error = nil
|
441
|
+
begin
|
442
|
+
JSON.parse(obj)
|
443
|
+
rescue => e
|
444
|
+
error = e
|
445
|
+
end
|
446
|
+
"contains invalid JSON: #{error}" if error
|
434
447
|
end
|
435
448
|
|
436
449
|
end
|
437
450
|
|
438
|
-
|
451
|
+
include Validators
|
452
|
+
extend Validators
|
453
|
+
|
454
|
+
end
|
455
|
+
|
456
|
+
class Client::V20060301 < Client
|
457
|
+
|
458
|
+
def self.object_method(method_name, verb, *args, &block)
|
459
|
+
bucket_method(method_name, verb, *args) do
|
460
|
+
configure_request do |req, options|
|
461
|
+
validate_key!(options[:key])
|
462
|
+
super(req, options)
|
463
|
+
req.key = options[:key]
|
464
|
+
end
|
465
|
+
|
466
|
+
instance_eval(&block) if block
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
public
|
471
|
+
|
472
|
+
# Creates a bucket.
|
473
|
+
# @overload create_bucket(options = {})
|
439
474
|
# @param [Hash] options
|
440
475
|
# @option options [required,String] :bucket_name
|
476
|
+
# @option options [String] :acl A canned ACL (e.g. 'private',
|
477
|
+
# 'public-read', etc). See the S3 API documentation for
|
478
|
+
# a complete list of valid values.
|
479
|
+
# @option options [String] :grant_read
|
480
|
+
# @option options [String] :grant_write
|
481
|
+
# @option options [String] :grant_read_acp
|
482
|
+
# @option options [String] :grant_write_acp
|
483
|
+
# @option options [String] :grant_full_control
|
441
484
|
# @return [Core::Response]
|
442
|
-
bucket_method(:
|
485
|
+
bucket_method(:create_bucket, :put, :header_options => {
|
486
|
+
:acl => 'x-amz-acl',
|
487
|
+
:grant_read => 'x-amz-grant-read',
|
488
|
+
:grant_write => 'x-amz-grant-write',
|
489
|
+
:grant_read_acp => 'x-amz-grant-read-acp',
|
490
|
+
:grant_write_acp => 'x-amz-grant-write-acp',
|
491
|
+
:grant_full_control => 'x-amz-grant-full-control',
|
492
|
+
}) do
|
493
|
+
|
443
494
|
configure_request do |req, options|
|
444
|
-
|
495
|
+
validate_bucket_name!(options[:bucket_name])
|
496
|
+
if location = options[:location_constraint]
|
497
|
+
xmlns = "http://s3.amazonaws.com/doc/#{API_VERSION}/"
|
498
|
+
req.body = <<-XML
|
499
|
+
<CreateBucketConfiguration xmlns="#{xmlns}">
|
500
|
+
<LocationConstraint>#{location}</LocationConstraint>
|
501
|
+
</CreateBucketConfiguration>
|
502
|
+
XML
|
503
|
+
end
|
445
504
|
super(req, options)
|
446
505
|
end
|
506
|
+
|
447
507
|
end
|
508
|
+
alias_method :put_bucket, :create_bucket
|
448
509
|
|
449
|
-
#
|
510
|
+
# @!method put_bucket_website(options = {})
|
450
511
|
# @param [Hash] options
|
512
|
+
# @option (see WebsiteConfiguration#initialize)
|
513
|
+
# @option options [required,String] :bucket_name
|
451
514
|
# @return [Core::Response]
|
452
|
-
|
515
|
+
bucket_method(:put_bucket_website, :put, 'website') do
|
453
516
|
|
454
517
|
configure_request do |req, options|
|
455
|
-
req.
|
456
|
-
|
518
|
+
req.body = Nokogiri::XML::Builder.new do |xml|
|
519
|
+
xml.WebsiteConfiguration(:xmlns => XMLNS) do
|
457
520
|
|
458
|
-
|
459
|
-
|
460
|
-
|
521
|
+
if redirect = options[:redirect_all_requests_to]
|
522
|
+
xml.RedirectAllRequestsTo do
|
523
|
+
xml.HostName(redirect[:host_name])
|
524
|
+
xml.Protocol(redirect[:protocol]) if redirect[:protocol]
|
525
|
+
end
|
526
|
+
end
|
461
527
|
|
462
|
-
|
463
|
-
|
464
|
-
|
528
|
+
if indx = options[:index_document]
|
529
|
+
xml.IndexDocument do
|
530
|
+
xml.Suffix(indx[:suffix])
|
531
|
+
end
|
532
|
+
end
|
465
533
|
|
466
|
-
|
534
|
+
if err = options[:error_document]
|
535
|
+
xml.ErrorDocument do
|
536
|
+
xml.Key(err[:key])
|
537
|
+
end
|
538
|
+
end
|
467
539
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
# or any object that responds to `#to_json`.
|
474
|
-
# @return [Core::Response]
|
475
|
-
bucket_method(:set_bucket_policy, :put, 'policy') do
|
540
|
+
rules = options[:routing_rules]
|
541
|
+
if rules.is_a?(Array) && !rules.empty?
|
542
|
+
xml.RoutingRules do
|
543
|
+
rules.each do |rule|
|
544
|
+
xml.RoutingRule do
|
476
545
|
|
477
|
-
|
478
|
-
|
546
|
+
redirect = rule[:redirect]
|
547
|
+
xml.Redirect do
|
548
|
+
xml.Protocol(redirect[:protocol]) if redirect[:protocol]
|
549
|
+
xml.HostName(redirect[:host_name]) if redirect[:host_name]
|
550
|
+
xml.ReplaceKeyPrefixWith(redirect[:replace_key_prefix_with]) if redirect[:replace_key_prefix_with]
|
551
|
+
xml.ReplaceKeyWith(redirect[:replace_key_with]) if redirect[:replace_key_with]
|
552
|
+
xml.HttpRedirectCode(redirect[:http_redirect_code]) if redirect[:http_redirect_code]
|
553
|
+
end
|
554
|
+
|
555
|
+
if condition = rule[:condition]
|
556
|
+
xml.Condition do
|
557
|
+
xml.KeyPrefixEquals(condition[:key_prefix_equals]) if condition[:key_prefix_equals]
|
558
|
+
xml.HttpErrorCodeReturnedEquals(condition[:http_error_code_returned_equals]) if condition[:http_error_code_returned_equals]
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
end
|
563
|
+
end
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
end
|
568
|
+
end.doc.root.to_xml
|
479
569
|
super(req, options)
|
480
|
-
policy = options[:policy]
|
481
|
-
policy = policy.to_json unless policy.respond_to?(:to_str)
|
482
|
-
req.body = policy
|
483
570
|
end
|
484
571
|
|
485
572
|
end
|
486
573
|
|
487
|
-
#
|
488
|
-
# @overload get_bucket_policy(options = {})
|
574
|
+
# @overload get_bucket_website(options = {})
|
489
575
|
# @param [Hash] options
|
490
576
|
# @option options [required,String] :bucket_name
|
491
577
|
# @return [Core::Response]
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
578
|
+
# * `:index_document` - (Hash)
|
579
|
+
# * `:suffix` - (String)
|
580
|
+
# * `:error_document` - (Hash)
|
581
|
+
# * `:key` - (String)
|
582
|
+
bucket_method(:get_bucket_website, :get, 'website', XML::GetBucketWebsite)
|
497
583
|
|
498
|
-
|
584
|
+
# @overload delete_bucket_website(options = {})
|
585
|
+
# @param [Hash] options
|
586
|
+
# @option options [required,String] :bucket_name
|
587
|
+
# @return [Core::Response]
|
588
|
+
bucket_method(:delete_bucket_website, :delete, 'website')
|
499
589
|
|
500
|
-
# Deletes
|
501
|
-
# @overload
|
590
|
+
# Deletes an empty bucket.
|
591
|
+
# @overload delete_bucket(options = {})
|
502
592
|
# @param [Hash] options
|
503
593
|
# @option options [required,String] :bucket_name
|
504
594
|
# @return [Core::Response]
|
505
|
-
bucket_method(:
|
595
|
+
bucket_method(:delete_bucket, :delete)
|
506
596
|
|
507
|
-
# @overload
|
597
|
+
# @overload set_bucket_lifecycle_configuration(options = {})
|
508
598
|
# @param [Hash] options
|
509
599
|
# @option options [required,String] :bucket_name
|
510
|
-
# @option options [required,String] :
|
511
|
-
# @option options [String] :mfa_delete
|
512
|
-
# @option options [String] :mfa
|
600
|
+
# @option options [required,String] :lifecycle_configuration
|
513
601
|
# @return [Core::Response]
|
514
|
-
bucket_method(:
|
602
|
+
bucket_method(:set_bucket_lifecycle_configuration, :put) do
|
515
603
|
|
516
604
|
configure_request do |req, options|
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
605
|
+
xml = options[:lifecycle_configuration]
|
606
|
+
req.add_param('lifecycle')
|
607
|
+
req.body = xml
|
608
|
+
req.headers['content-md5'] = md5(xml)
|
609
|
+
super(req, options)
|
610
|
+
end
|
521
611
|
|
522
|
-
|
523
|
-
mfa_delete = options[:mfa_delete].to_s.downcase.capitalize if options[:mfa_delete]
|
612
|
+
end
|
524
613
|
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
end
|
531
|
-
end.doc.root.to_xml
|
614
|
+
# @overload get_bucket_lifecycle_configuration(options = {})
|
615
|
+
# @param [Hash] options
|
616
|
+
# @option options [required,String] :bucket_name
|
617
|
+
# @return [Core::Response]
|
618
|
+
bucket_method(:get_bucket_lifecycle_configuration, :get) do
|
532
619
|
|
620
|
+
configure_request do |req, options|
|
621
|
+
req.add_param('lifecycle')
|
533
622
|
super(req, options)
|
534
623
|
end
|
535
624
|
|
625
|
+
process_response do |resp|
|
626
|
+
xml = resp.http_response.body
|
627
|
+
resp.data = XML::GetBucketLifecycleConfiguration.parse(xml)
|
628
|
+
end
|
629
|
+
|
536
630
|
end
|
537
631
|
|
538
|
-
#
|
539
|
-
# @overload get_bucket_location(options = {})
|
632
|
+
# @overload delete_bucket_lifecycle_configuration(options = {})
|
540
633
|
# @param [Hash] options
|
541
634
|
# @option options [required,String] :bucket_name
|
542
635
|
# @return [Core::Response]
|
543
|
-
bucket_method(:
|
636
|
+
bucket_method(:delete_bucket_lifecycle_configuration, :delete) do
|
544
637
|
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
response.data[:location_constraint] = matches ? matches[1] : nil
|
638
|
+
configure_request do |req, options|
|
639
|
+
req.add_param('lifecycle')
|
640
|
+
super(req, options)
|
549
641
|
end
|
550
642
|
|
551
643
|
end
|
552
644
|
|
553
|
-
# @overload
|
645
|
+
# @overload put_bucket_cors(options = {})
|
554
646
|
# @param [Hash] options
|
555
647
|
# @option options [required,String] :bucket_name
|
556
|
-
# @option options [
|
557
|
-
#
|
558
|
-
#
|
559
|
-
#
|
560
|
-
#
|
561
|
-
#
|
562
|
-
#
|
563
|
-
#
|
564
|
-
#
|
565
|
-
#
|
566
|
-
#
|
567
|
-
#
|
568
|
-
#
|
569
|
-
#
|
570
|
-
#
|
571
|
-
#
|
572
|
-
#
|
573
|
-
#
|
574
|
-
#
|
575
|
-
#
|
576
|
-
#
|
648
|
+
# @option options [required,Array<Hash>] :rules An array of rule hashes.
|
649
|
+
# * `:id` - (String) A unique identifier for the rule. The ID
|
650
|
+
# value can be up to 255 characters long. The IDs help you find
|
651
|
+
# a rule in the configuration.
|
652
|
+
# * `:allowed_methods` - (required,Array<String>) A list of HTTP
|
653
|
+
# methods that you want to allow the origin to execute.
|
654
|
+
# Each rule must identify at least one method.
|
655
|
+
# * `:allowed_origins` - (required,Array<String>) A list of origins
|
656
|
+
# you want to allow cross-domain requests from. This can
|
657
|
+
# contain at most one * wild character.
|
658
|
+
# * `:allowed_headers` - (Array<String>) A list of headers allowed
|
659
|
+
# in a pre-flight OPTIONS request via the
|
660
|
+
# Access-Control-Request-Headers header. Each header name
|
661
|
+
# specified in the Access-Control-Request-Headers header must
|
662
|
+
# have a corresponding entry in the rule.
|
663
|
+
# Amazon S3 will send only the allowed headers in a response
|
664
|
+
# that were requested. This can contain at most one * wild
|
665
|
+
# character.
|
666
|
+
# * `:max_age_seconds` - (Integer) The time in seconds that your
|
667
|
+
# browser is to cache the preflight response for the specified
|
668
|
+
# resource.
|
669
|
+
# * `:expose_headers` - (Array<String>) One or more headers in
|
670
|
+
# the response that you want customers to be able to access
|
671
|
+
# from their applications (for example, from a JavaScript
|
672
|
+
# XMLHttpRequest object).
|
577
673
|
# @return [Core::Response]
|
578
|
-
bucket_method(:
|
674
|
+
bucket_method(:put_bucket_cors, :put) do
|
579
675
|
configure_request do |req, options|
|
580
676
|
|
581
|
-
req.add_param('
|
677
|
+
req.add_param('cors')
|
582
678
|
|
583
|
-
xml = Nokogiri::XML::Builder.new
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
xml.TargetBucket(options[:target_bucket])
|
588
|
-
xml.TargetPrefix(options[:target_prefix])
|
589
|
-
unless options[:grants].nil?
|
679
|
+
xml = Nokogiri::XML::Builder.new do |xml|
|
680
|
+
xml.CORSConfiguration do
|
681
|
+
options[:rules].each do |rule|
|
682
|
+
xml.CORSRule do
|
590
683
|
|
591
|
-
xml.
|
592
|
-
options[:grants].each do |grant|
|
593
|
-
xml.Grant do
|
594
|
-
if !grant[:email_address].nil?
|
595
|
-
xml.Grantee('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
596
|
-
'xsi:type' => 'AmazonCustomerByEmail') do
|
597
|
-
xml.EmailAddress(grant[:email_address])
|
598
|
-
end
|
599
|
-
elsif !grant[:uri].nil?
|
600
|
-
xml.Grantee('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
601
|
-
'xsi:type' => 'Group') do
|
602
|
-
xml.URI(grant[:uri])
|
603
|
-
end
|
604
|
-
elsif !grant[:id].nil?
|
605
|
-
xml.Grantee('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
606
|
-
'xsi:type' => 'CanonicalUser') do
|
607
|
-
xml.ID(grant[:id])
|
608
|
-
end
|
609
|
-
end
|
684
|
+
xml.ID(rule[:id]) if rule[:id]
|
610
685
|
|
611
|
-
|
612
|
-
|
613
|
-
|
686
|
+
(rule[:allowed_methods] || []).each do |method|
|
687
|
+
xml.AllowedMethod(method)
|
688
|
+
end
|
689
|
+
|
690
|
+
(rule[:allowed_origins] || []).each do |origin|
|
691
|
+
xml.AllowedOrigin(origin)
|
692
|
+
end
|
693
|
+
|
694
|
+
(rule[:allowed_headers] || []).each do |header|
|
695
|
+
xml.AllowedHeader(header)
|
696
|
+
end
|
697
|
+
|
698
|
+
xml.MaxAgeSeconds(rule[:max_age_seconds]) if
|
699
|
+
rule[:max_age_seconds]
|
700
|
+
|
701
|
+
(rule[:expose_headers] || []).each do |header|
|
702
|
+
xml.ExposeHeader(header)
|
614
703
|
end
|
704
|
+
|
615
705
|
end
|
616
706
|
end
|
617
707
|
end
|
618
|
-
end
|
708
|
+
end.doc.root.to_xml
|
619
709
|
|
620
|
-
xml = xml.doc.root.to_xml
|
621
710
|
req.body = xml
|
622
711
|
req.headers['content-md5'] = md5(xml)
|
623
|
-
|
712
|
+
|
624
713
|
super(req, options)
|
625
714
|
|
626
715
|
end
|
627
716
|
end
|
628
717
|
|
629
|
-
#
|
630
|
-
# @overload get_bucket_logging(options = {})
|
718
|
+
# @overload get_bucket_cors(options = {})
|
631
719
|
# @param [Hash] options
|
632
720
|
# @option options [required,String] :bucket_name
|
633
721
|
# @return [Core::Response]
|
634
|
-
bucket_method(:
|
635
|
-
XML::GetBucketLogging)
|
722
|
+
bucket_method(:get_bucket_cors, :get) do
|
636
723
|
|
637
|
-
|
724
|
+
configure_request do |req, options|
|
725
|
+
req.add_param('cors')
|
726
|
+
super(req, options)
|
727
|
+
end
|
728
|
+
|
729
|
+
process_response do |resp|
|
730
|
+
resp.data = XML::GetBucketCors.parse(resp.http_response.body)
|
731
|
+
end
|
732
|
+
|
733
|
+
end
|
734
|
+
|
735
|
+
# @overload delete_bucket_cors(options = {})
|
638
736
|
# @param [Hash] options
|
639
737
|
# @option options [required,String] :bucket_name
|
640
738
|
# @return [Core::Response]
|
641
|
-
bucket_method(:
|
642
|
-
|
739
|
+
bucket_method(:delete_bucket_cors, :delete) do
|
740
|
+
configure_request do |req, options|
|
741
|
+
req.add_param('cors')
|
742
|
+
super(req, options)
|
743
|
+
end
|
744
|
+
end
|
643
745
|
|
644
|
-
# @overload
|
746
|
+
# @overload put_bucket_tagging(options = {})
|
645
747
|
# @param [Hash] options
|
646
748
|
# @option options [required,String] :bucket_name
|
647
|
-
# @option options [
|
648
|
-
# @option options [String] :delimiter
|
649
|
-
# @option options [String] :max_keys
|
650
|
-
# @option options [String] :key_marker
|
651
|
-
# @option options [String] :version_id_marker
|
749
|
+
# @option options [Hash] :tags
|
652
750
|
# @return [Core::Response]
|
653
|
-
bucket_method(:
|
654
|
-
XML::ListObjectVersions) do
|
655
|
-
|
751
|
+
bucket_method(:put_bucket_tagging, :put) do
|
656
752
|
configure_request do |req, options|
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
753
|
+
|
754
|
+
req.add_param('tagging')
|
755
|
+
|
756
|
+
xml = Nokogiri::XML::Builder.new
|
757
|
+
xml.Tagging do |xml|
|
758
|
+
xml.TagSet do
|
759
|
+
options[:tags].each_pair do |key,value|
|
760
|
+
xml.Tag do
|
761
|
+
xml.Key(key)
|
762
|
+
xml.Value(value)
|
763
|
+
end
|
764
|
+
end
|
662
765
|
end
|
663
766
|
end
|
664
|
-
end
|
665
767
|
|
768
|
+
xml = xml.doc.root.to_xml
|
769
|
+
req.body = xml
|
770
|
+
req.headers['content-md5'] = md5(xml)
|
771
|
+
|
772
|
+
super(req, options)
|
773
|
+
|
774
|
+
end
|
666
775
|
end
|
667
776
|
|
668
|
-
#
|
669
|
-
# via one of the following methods:
|
670
|
-
#
|
671
|
-
# * as a canned ACL (via `:acl`)
|
672
|
-
# * as a list of grants (via the `:grant_*` options)
|
673
|
-
# * as an access control policy document (via `:access_control_policy`)
|
674
|
-
#
|
675
|
-
# @example Using a canned acl
|
676
|
-
# s3_client.put_bucket_acl(
|
677
|
-
# :bucket_name => 'bucket-name',
|
678
|
-
# :acl => 'public-read')
|
679
|
-
#
|
680
|
-
# @example Using grants
|
681
|
-
# s3_client.put_bucket_acl(
|
682
|
-
# :bucket_name => 'bucket-name',
|
683
|
-
# :grant_read => 'uri="http://acs.amazonaws.com/groups/global/AllUsers"',
|
684
|
-
# :grant_full_control => 'emailAddress="xyz@amazon.com", id="8a9...fa7"')
|
685
|
-
#
|
686
|
-
# @example Using an access control policy document
|
687
|
-
# policy_xml = <<-XML
|
688
|
-
# <AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
689
|
-
# <Owner>
|
690
|
-
# <ID>852b113e7a2f25102679df27bb0ae12b3f85be6BucketOwnerCanonicalUserID</ID>
|
691
|
-
# <DisplayName>OwnerDisplayName</DisplayName>
|
692
|
-
# </Owner>
|
693
|
-
# <AccessControlList>
|
694
|
-
# <Grant>
|
695
|
-
# <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
|
696
|
-
# <ID>BucketOwnerCanonicalUserID</ID>
|
697
|
-
# <DisplayName>OwnerDisplayName</DisplayName>
|
698
|
-
# </Grantee>
|
699
|
-
# <Permission>FULL_CONTROL</Permission>
|
700
|
-
# </Grant>
|
701
|
-
# <Grant>
|
702
|
-
# <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group">
|
703
|
-
# <URI xmlns="">http://acs.amazonaws.com/groups/global/AllUsers</URI>
|
704
|
-
# </Grantee>
|
705
|
-
# <Permission xmlns="">READ</Permission>
|
706
|
-
# </Grant>
|
707
|
-
# </AccessControlList>
|
708
|
-
# </AccessControlPolicy>
|
709
|
-
#
|
710
|
-
# XML
|
711
|
-
# s3_client.put_bucket_acl(
|
712
|
-
# :bucket_name => 'bucket-name',
|
713
|
-
# :access_control_policy => policy_xml)
|
714
|
-
#
|
715
|
-
# @overload put_bucket_acl(options = {})
|
777
|
+
# @overload get_bucket_tagging(options = {})
|
716
778
|
# @param [Hash] options
|
717
779
|
# @option options [required,String] :bucket_name
|
718
|
-
# @option options [String] :access_control_policy An access control
|
719
|
-
# policy description as a string of XML. See the S3 API
|
720
|
-
# documentation for a description.
|
721
|
-
# @option options [String] :acl A canned ACL (e.g. 'private',
|
722
|
-
# 'public-read', etc). See the S3 API documentation for
|
723
|
-
# a complete list of valid values.
|
724
|
-
# @option options [String] :grant_read
|
725
|
-
# @option options [String] :grant_write
|
726
|
-
# @option options [String] :grant_read_acp
|
727
|
-
# @option options [String] :grant_write_acp
|
728
|
-
# @option options [String] :grant_full_control
|
729
780
|
# @return [Core::Response]
|
730
|
-
bucket_method(:
|
731
|
-
:acl => 'x-amz-acl',
|
732
|
-
:grant_read => 'x-amz-grant-read',
|
733
|
-
:grant_write => 'x-amz-grant-write',
|
734
|
-
:grant_read_acp => 'x-amz-grant-read-acp',
|
735
|
-
:grant_write_acp => 'x-amz-grant-write-acp',
|
736
|
-
:grant_full_control => 'x-amz-grant-full-control',
|
737
|
-
}) do
|
781
|
+
bucket_method(:get_bucket_tagging, :get) do
|
738
782
|
|
739
783
|
configure_request do |req, options|
|
740
|
-
|
741
|
-
require_acl!(options)
|
784
|
+
req.add_param('tagging')
|
742
785
|
super(req, options)
|
743
|
-
|
744
|
-
|
786
|
+
end
|
787
|
+
|
788
|
+
process_response do |resp|
|
789
|
+
resp.data = XML::GetBucketTagging.parse(resp.http_response.body)
|
745
790
|
end
|
746
791
|
|
747
792
|
end
|
748
|
-
alias_method :set_bucket_acl, :put_bucket_acl
|
749
793
|
|
750
|
-
#
|
751
|
-
# @overload get_bucket_acl(options = {})
|
794
|
+
# @overload delete_bucket_tagging(options = {})
|
752
795
|
# @param [Hash] options
|
753
796
|
# @option options [required,String] :bucket_name
|
754
797
|
# @return [Core::Response]
|
755
|
-
bucket_method(:
|
756
|
-
|
757
|
-
|
758
|
-
|
798
|
+
bucket_method(:delete_bucket_tagging, :delete) do
|
799
|
+
configure_request do |req, options|
|
800
|
+
req.add_param('tagging')
|
801
|
+
super(req, options)
|
802
|
+
end
|
803
|
+
end
|
804
|
+
|
805
|
+
# @overload list_buckets(options = {})
|
806
|
+
# @param [Hash] options
|
807
|
+
# @return [Core::Response]
|
808
|
+
add_client_request_method(:list_buckets) do
|
809
|
+
|
810
|
+
configure_request do |req, options|
|
811
|
+
req.http_method = "GET"
|
812
|
+
end
|
813
|
+
|
814
|
+
process_response do |resp|
|
815
|
+
resp.data = XML::ListBuckets.parse(resp.http_response.body)
|
816
|
+
end
|
817
|
+
|
818
|
+
simulate_response do |resp|
|
819
|
+
resp.data = Core::XML::Parser.new(XML::ListBuckets.rules).simulate
|
820
|
+
end
|
821
|
+
|
822
|
+
end
|
823
|
+
|
824
|
+
# Sets the access policy for a bucket.
|
825
|
+
# @overload set_bucket_policy(options = {})
|
826
|
+
# @param [Hash] options
|
827
|
+
# @option options [required,String] :bucket_name
|
828
|
+
# @option options [required,String] :policy This can be a String
|
829
|
+
# or any object that responds to `#to_json`.
|
830
|
+
# @return [Core::Response]
|
831
|
+
bucket_method(:set_bucket_policy, :put, 'policy') do
|
832
|
+
|
833
|
+
configure_request do |req, options|
|
834
|
+
require_policy!(options[:policy])
|
835
|
+
super(req, options)
|
836
|
+
policy = options[:policy]
|
837
|
+
policy = policy.to_json unless policy.respond_to?(:to_str)
|
838
|
+
req.body = policy
|
839
|
+
end
|
840
|
+
|
841
|
+
end
|
842
|
+
|
843
|
+
# Gets the access policy for a bucket.
|
844
|
+
# @overload get_bucket_policy(options = {})
|
845
|
+
# @param [Hash] options
|
846
|
+
# @option options [required,String] :bucket_name
|
847
|
+
# @return [Core::Response]
|
848
|
+
bucket_method(:get_bucket_policy, :get, 'policy') do
|
849
|
+
|
850
|
+
process_response do |resp|
|
851
|
+
resp.data[:policy] = resp.http_response.body
|
852
|
+
end
|
853
|
+
|
854
|
+
end
|
855
|
+
|
856
|
+
# Deletes the access policy for a bucket.
|
857
|
+
# @overload delete_bucket_policy(options = {})
|
858
|
+
# @param [Hash] options
|
859
|
+
# @option options [required,String] :bucket_name
|
860
|
+
# @return [Core::Response]
|
861
|
+
bucket_method(:delete_bucket_policy, :delete, 'policy')
|
862
|
+
|
863
|
+
# @overload set_bucket_versioning(options = {})
|
864
|
+
# @param [Hash] options
|
865
|
+
# @option options [required,String] :bucket_name
|
866
|
+
# @option options [required,String] :state
|
867
|
+
# @option options [String] :mfa_delete
|
868
|
+
# @option options [String] :mfa
|
869
|
+
# @return [Core::Response]
|
870
|
+
bucket_method(:set_bucket_versioning, :put, 'versioning', :header_options => { :mfa => "x-amz-mfa" }) do
|
871
|
+
|
872
|
+
configure_request do |req, options|
|
873
|
+
state = options[:state].to_s.downcase.capitalize
|
874
|
+
unless state =~ /^(Enabled|Suspended)$/
|
875
|
+
raise ArgumentError, "invalid versioning state `#{state}`"
|
876
|
+
end
|
877
|
+
|
878
|
+
# Leave validation of MFA options to S3
|
879
|
+
mfa_delete = options[:mfa_delete].to_s.downcase.capitalize if options[:mfa_delete]
|
880
|
+
|
881
|
+
# Generate XML request for versioning
|
882
|
+
req.body = Nokogiri::XML::Builder.new do |xml|
|
883
|
+
xml.VersioningConfiguration('xmlns' => XMLNS) do
|
884
|
+
xml.Status(state)
|
885
|
+
xml.MfaDelete(mfa_delete) if mfa_delete
|
886
|
+
end
|
887
|
+
end.doc.root.to_xml
|
888
|
+
|
889
|
+
super(req, options)
|
890
|
+
end
|
891
|
+
|
892
|
+
end
|
893
|
+
|
894
|
+
# Gets the bucket's location constraint.
|
895
|
+
# @overload get_bucket_location(options = {})
|
896
|
+
# @param [Hash] options
|
897
|
+
# @option options [required,String] :bucket_name
|
898
|
+
# @return [Core::Response]
|
899
|
+
bucket_method(:get_bucket_location, :get, 'location') do
|
900
|
+
|
901
|
+
process_response do |response|
|
902
|
+
regex = />(.*)<\/LocationConstraint>/
|
903
|
+
matches = response.http_response.body.to_s.match(regex)
|
904
|
+
response.data[:location_constraint] = matches ? matches[1] : nil
|
905
|
+
end
|
906
|
+
|
907
|
+
end
|
908
|
+
|
909
|
+
# @overload put_bucket_logging(options = {})
|
910
|
+
# @param [Hash] options
|
911
|
+
# @option options [required,String] :bucket_name
|
912
|
+
# @option options [Boolean] :logging_enabled Set to true if turning on
|
913
|
+
# bucket logging. If not set or false, all of the following options
|
914
|
+
# will be ignored.
|
915
|
+
# @option options [String] :target_bucket The name of the bucket in
|
916
|
+
# which you want Amazon S3 to store server access logs. You can push
|
917
|
+
# logs to any bucket you own, including the bucket being logged.
|
918
|
+
# @option options [String] :target_prefix Allows you to specify a prefix
|
919
|
+
# for the keys that the log files will be stored under. Recommended
|
920
|
+
# if you will be writing logs from multiple buckets to the same target
|
921
|
+
# bucket.
|
922
|
+
# @option options [Array<Hash>] :grants An array of hashes specifying
|
923
|
+
# permission grantees. For each hash, specify ONLY ONE of :id, :uri,
|
924
|
+
# or :email_address.
|
925
|
+
# * `:email_address` - (String) E-mail address of the person being
|
926
|
+
# granted logging permissions.
|
927
|
+
# * `:id` - (String) The canonical user ID of the grantee.
|
928
|
+
# * `:uri` - (String) URI of the grantee group.
|
929
|
+
# * `:permission` - (String) Logging permissions given to the Grantee
|
930
|
+
# for the bucket. The bucket owner is automatically granted FULL_CONTROL
|
931
|
+
# to all logs delivered to the bucket. This optional element enables
|
932
|
+
# you grant access to others. Valid Values: FULL_CONTROL | READ | WRITE
|
933
|
+
# @return [Core::Response]
|
934
|
+
bucket_method(:put_bucket_logging, :put) do
|
935
|
+
configure_request do |req, options|
|
936
|
+
|
937
|
+
req.add_param('logging')
|
938
|
+
|
939
|
+
xml = Nokogiri::XML::Builder.new
|
940
|
+
xml.BucketLoggingStatus('xmlns' => XMLNS) do |xml|
|
941
|
+
if options[:logging_enabled] == true
|
942
|
+
xml.LoggingEnabled do
|
943
|
+
xml.TargetBucket(options[:target_bucket])
|
944
|
+
xml.TargetPrefix(options[:target_prefix])
|
945
|
+
unless options[:grants].nil?
|
946
|
+
|
947
|
+
xml.TargetGrants do
|
948
|
+
options[:grants].each do |grant|
|
949
|
+
xml.Grant do
|
950
|
+
if !grant[:email_address].nil?
|
951
|
+
xml.Grantee('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
952
|
+
'xsi:type' => 'AmazonCustomerByEmail') do
|
953
|
+
xml.EmailAddress(grant[:email_address])
|
954
|
+
end
|
955
|
+
elsif !grant[:uri].nil?
|
956
|
+
xml.Grantee('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
957
|
+
'xsi:type' => 'Group') do
|
958
|
+
xml.URI(grant[:uri])
|
959
|
+
end
|
960
|
+
elsif !grant[:id].nil?
|
961
|
+
xml.Grantee('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
962
|
+
'xsi:type' => 'CanonicalUser') do
|
963
|
+
xml.ID(grant[:id])
|
964
|
+
end
|
965
|
+
end
|
966
|
+
|
967
|
+
xml.Permission(grant[:permission])
|
968
|
+
end
|
969
|
+
end
|
970
|
+
end
|
971
|
+
end
|
972
|
+
end
|
973
|
+
end
|
974
|
+
end
|
975
|
+
|
976
|
+
xml = xml.doc.root.to_xml
|
977
|
+
req.body = xml
|
978
|
+
req.headers['content-md5'] = md5(xml)
|
979
|
+
|
980
|
+
super(req, options)
|
981
|
+
|
982
|
+
end
|
983
|
+
end
|
984
|
+
|
985
|
+
# Gets the bucket's logging status.
|
986
|
+
# @overload get_bucket_logging(options = {})
|
987
|
+
# @param [Hash] options
|
988
|
+
# @option options [required,String] :bucket_name
|
989
|
+
# @return [Core::Response]
|
990
|
+
bucket_method(:get_bucket_logging, :get, 'logging',
|
991
|
+
XML::GetBucketLogging)
|
992
|
+
|
993
|
+
# @overload get_bucket_versioning(options = {})
|
994
|
+
# @param [Hash] options
|
995
|
+
# @option options [required,String] :bucket_name
|
996
|
+
# @return [Core::Response]
|
997
|
+
bucket_method(:get_bucket_versioning, :get, 'versioning',
|
998
|
+
XML::GetBucketVersioning)
|
999
|
+
|
1000
|
+
# @overload list_object_versions(options = {})
|
1001
|
+
# @param [Hash] options
|
1002
|
+
# @option options [required,String] :bucket_name
|
1003
|
+
# @option options [String] :prefix
|
1004
|
+
# @option options [String] :delimiter
|
1005
|
+
# @option options [String] :max_keys
|
1006
|
+
# @option options [String] :key_marker
|
1007
|
+
# @option options [String] :version_id_marker
|
1008
|
+
# @return [Core::Response]
|
1009
|
+
bucket_method(:list_object_versions, :get, 'versions',
|
1010
|
+
XML::ListObjectVersions) do
|
1011
|
+
|
1012
|
+
configure_request do |req, options|
|
1013
|
+
super(req, options)
|
1014
|
+
params = %w(delimiter key_marker max_keys prefix version_id_marker)
|
1015
|
+
params.each do |param|
|
1016
|
+
if options[param.to_sym]
|
1017
|
+
req.add_param(param.gsub(/_/, '-'), options[param.to_sym])
|
1018
|
+
end
|
1019
|
+
end
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
# Sets the access control list for a bucket. You must specify an ACL
|
1025
|
+
# via one of the following methods:
|
759
1026
|
#
|
760
1027
|
# * as a canned ACL (via `:acl`)
|
761
1028
|
# * as a list of grants (via the `:grant_*` options)
|
762
1029
|
# * as an access control policy document (via `:access_control_policy`)
|
763
1030
|
#
|
764
1031
|
# @example Using a canned acl
|
765
|
-
# s3_client.
|
1032
|
+
# s3_client.put_bucket_acl(
|
766
1033
|
# :bucket_name => 'bucket-name',
|
767
|
-
# :key => 'object-key',
|
768
1034
|
# :acl => 'public-read')
|
769
1035
|
#
|
770
1036
|
# @example Using grants
|
771
1037
|
# s3_client.put_bucket_acl(
|
772
1038
|
# :bucket_name => 'bucket-name',
|
773
|
-
# :key => 'object-key',
|
774
1039
|
# :grant_read => 'uri="http://acs.amazonaws.com/groups/global/AllUsers"',
|
775
1040
|
# :grant_full_control => 'emailAddress="xyz@amazon.com", id="8a9...fa7"')
|
776
1041
|
#
|
@@ -801,13 +1066,11 @@ module AWS
|
|
801
1066
|
# XML
|
802
1067
|
# s3_client.put_bucket_acl(
|
803
1068
|
# :bucket_name => 'bucket-name',
|
804
|
-
# :key => 'object-key',
|
805
1069
|
# :access_control_policy => policy_xml)
|
806
1070
|
#
|
807
|
-
# @overload
|
1071
|
+
# @overload put_bucket_acl(options = {})
|
808
1072
|
# @param [Hash] options
|
809
1073
|
# @option options [required,String] :bucket_name
|
810
|
-
# @option options [required,String] :key
|
811
1074
|
# @option options [String] :access_control_policy An access control
|
812
1075
|
# policy description as a string of XML. See the S3 API
|
813
1076
|
# documentation for a description.
|
@@ -820,7 +1083,7 @@ module AWS
|
|
820
1083
|
# @option options [String] :grant_write_acp
|
821
1084
|
# @option options [String] :grant_full_control
|
822
1085
|
# @return [Core::Response]
|
823
|
-
|
1086
|
+
bucket_method(:put_bucket_acl, :put, 'acl', :header_options => {
|
824
1087
|
:acl => 'x-amz-acl',
|
825
1088
|
:grant_read => 'x-amz-grant-read',
|
826
1089
|
:grant_write => 'x-amz-grant-write',
|
@@ -838,23 +1101,116 @@ module AWS
|
|
838
1101
|
end
|
839
1102
|
|
840
1103
|
end
|
841
|
-
alias_method :
|
1104
|
+
alias_method :set_bucket_acl, :put_bucket_acl
|
842
1105
|
|
843
|
-
# Gets the access control list for
|
844
|
-
# @overload
|
1106
|
+
# Gets the access control list for a bucket.
|
1107
|
+
# @overload get_bucket_acl(options = {})
|
845
1108
|
# @param [Hash] options
|
846
1109
|
# @option options [required,String] :bucket_name
|
847
|
-
# @option options [required,String] :key
|
848
1110
|
# @return [Core::Response]
|
849
|
-
|
1111
|
+
bucket_method(:get_bucket_acl, :get, 'acl', XML::GetBucketAcl)
|
850
1112
|
|
851
|
-
#
|
852
|
-
#
|
853
|
-
#
|
854
|
-
#
|
855
|
-
#
|
856
|
-
#
|
857
|
-
#
|
1113
|
+
# Sets the access control list for an object. You must specify an ACL
|
1114
|
+
# via one of the following methods:
|
1115
|
+
#
|
1116
|
+
# * as a canned ACL (via `:acl`)
|
1117
|
+
# * as a list of grants (via the `:grant_*` options)
|
1118
|
+
# * as an access control policy document (via `:access_control_policy`)
|
1119
|
+
#
|
1120
|
+
# @example Using a canned acl
|
1121
|
+
# s3_client.put_object_acl(
|
1122
|
+
# :bucket_name => 'bucket-name',
|
1123
|
+
# :key => 'object-key',
|
1124
|
+
# :acl => 'public-read')
|
1125
|
+
#
|
1126
|
+
# @example Using grants
|
1127
|
+
# s3_client.put_bucket_acl(
|
1128
|
+
# :bucket_name => 'bucket-name',
|
1129
|
+
# :key => 'object-key',
|
1130
|
+
# :grant_read => 'uri="http://acs.amazonaws.com/groups/global/AllUsers"',
|
1131
|
+
# :grant_full_control => 'emailAddress="xyz@amazon.com", id="8a9...fa7"')
|
1132
|
+
#
|
1133
|
+
# @example Using an access control policy document
|
1134
|
+
# policy_xml = <<-XML
|
1135
|
+
# <AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
1136
|
+
# <Owner>
|
1137
|
+
# <ID>852b113e7a2f25102679df27bb0ae12b3f85be6BucketOwnerCanonicalUserID</ID>
|
1138
|
+
# <DisplayName>OwnerDisplayName</DisplayName>
|
1139
|
+
# </Owner>
|
1140
|
+
# <AccessControlList>
|
1141
|
+
# <Grant>
|
1142
|
+
# <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
|
1143
|
+
# <ID>BucketOwnerCanonicalUserID</ID>
|
1144
|
+
# <DisplayName>OwnerDisplayName</DisplayName>
|
1145
|
+
# </Grantee>
|
1146
|
+
# <Permission>FULL_CONTROL</Permission>
|
1147
|
+
# </Grant>
|
1148
|
+
# <Grant>
|
1149
|
+
# <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group">
|
1150
|
+
# <URI xmlns="">http://acs.amazonaws.com/groups/global/AllUsers</URI>
|
1151
|
+
# </Grantee>
|
1152
|
+
# <Permission xmlns="">READ</Permission>
|
1153
|
+
# </Grant>
|
1154
|
+
# </AccessControlList>
|
1155
|
+
# </AccessControlPolicy>
|
1156
|
+
#
|
1157
|
+
# XML
|
1158
|
+
# s3_client.put_bucket_acl(
|
1159
|
+
# :bucket_name => 'bucket-name',
|
1160
|
+
# :key => 'object-key',
|
1161
|
+
# :access_control_policy => policy_xml)
|
1162
|
+
#
|
1163
|
+
# @overload put_object_acl(options = {})
|
1164
|
+
# @param [Hash] options
|
1165
|
+
# @option options [required,String] :bucket_name
|
1166
|
+
# @option options [required,String] :key
|
1167
|
+
# @option options [String] :access_control_policy An access control
|
1168
|
+
# policy description as a string of XML. See the S3 API
|
1169
|
+
# documentation for a description.
|
1170
|
+
# @option options [String] :acl A canned ACL (e.g. 'private',
|
1171
|
+
# 'public-read', etc). See the S3 API documentation for
|
1172
|
+
# a complete list of valid values.
|
1173
|
+
# @option options [String] :grant_read
|
1174
|
+
# @option options [String] :grant_write
|
1175
|
+
# @option options [String] :grant_read_acp
|
1176
|
+
# @option options [String] :grant_write_acp
|
1177
|
+
# @option options [String] :grant_full_control
|
1178
|
+
# @return [Core::Response]
|
1179
|
+
object_method(:put_object_acl, :put, 'acl', :header_options => {
|
1180
|
+
:acl => 'x-amz-acl',
|
1181
|
+
:grant_read => 'x-amz-grant-read',
|
1182
|
+
:grant_write => 'x-amz-grant-write',
|
1183
|
+
:grant_read_acp => 'x-amz-grant-read-acp',
|
1184
|
+
:grant_write_acp => 'x-amz-grant-write-acp',
|
1185
|
+
:grant_full_control => 'x-amz-grant-full-control',
|
1186
|
+
}) do
|
1187
|
+
|
1188
|
+
configure_request do |req, options|
|
1189
|
+
move_access_control_policy(options)
|
1190
|
+
require_acl!(options)
|
1191
|
+
super(req, options)
|
1192
|
+
req.body = options[:access_control_policy] if
|
1193
|
+
options[:access_control_policy]
|
1194
|
+
end
|
1195
|
+
|
1196
|
+
end
|
1197
|
+
alias_method :set_object_acl, :put_object_acl
|
1198
|
+
|
1199
|
+
# Gets the access control list for an object.
|
1200
|
+
# @overload get_object_acl(options = {})
|
1201
|
+
# @param [Hash] options
|
1202
|
+
# @option options [required,String] :bucket_name
|
1203
|
+
# @option options [required,String] :key
|
1204
|
+
# @return [Core::Response]
|
1205
|
+
object_method(:get_object_acl, :get, 'acl', XML::GetBucketAcl)
|
1206
|
+
|
1207
|
+
# Puts data into an object, replacing the current contents.
|
1208
|
+
#
|
1209
|
+
# s3_client.put_object({
|
1210
|
+
# :bucket_name => 'bucket-name',
|
1211
|
+
# :key => 'readme.txt',
|
1212
|
+
# :data => 'This is the readme for ...',
|
1213
|
+
# })
|
858
1214
|
#
|
859
1215
|
# @overload put_object(options = {})
|
860
1216
|
# @param [Hash] options
|
@@ -1270,549 +1626,194 @@ module AWS
|
|
1270
1626
|
|
1271
1627
|
end
|
1272
1628
|
|
1273
|
-
process_response do |resp|
|
1274
|
-
extract_object_headers(resp)
|
1275
|
-
end
|
1276
|
-
|
1277
|
-
simulate_response do |response|
|
1278
|
-
response.data[:etag] = 'abc123'
|
1279
|
-
end
|
1280
|
-
end
|
1281
|
-
|
1282
|
-
# @overload complete_multipart_upload(options = {})
|
1283
|
-
# @param [Hash] options
|
1284
|
-
# @option options [required,String] :bucket_name
|
1285
|
-
# @option options [required,String] :key
|
1286
|
-
# @option options [required,String] :upload_id
|
1287
|
-
# @option options [required,Array<Hash>] :parts An array of hashes
|
1288
|
-
# with the following keys:
|
1289
|
-
# * `:part_number` [Integer] - *required*
|
1290
|
-
# * `:etag` [String] - *required*
|
1291
|
-
# @return [Core::Response]
|
1292
|
-
object_method(:complete_multipart_upload, :post,
|
1293
|
-
XML::CompleteMultipartUpload) do
|
1294
|
-
configure_request do |req, options|
|
1295
|
-
require_upload_id!(options[:upload_id])
|
1296
|
-
validate_parts!(options[:parts])
|
1297
|
-
super(req, options)
|
1298
|
-
req.add_param('uploadId', options[:upload_id])
|
1299
|
-
|
1300
|
-
req.body = Nokogiri::XML::Builder.new do |xml|
|
1301
|
-
xml.CompleteMultipartUpload do
|
1302
|
-
options[:parts].each do |part|
|
1303
|
-
xml.Part do
|
1304
|
-
xml.PartNumber(part[:part_number])
|
1305
|
-
xml.ETag(part[:etag])
|
1306
|
-
end
|
1307
|
-
end
|
1308
|
-
end
|
1309
|
-
end.doc.root.to_xml
|
1310
|
-
|
1311
|
-
end
|
1312
|
-
|
1313
|
-
process_response do |resp|
|
1314
|
-
extract_object_headers(resp)
|
1315
|
-
end
|
1316
|
-
|
1317
|
-
simulate_response do |response|
|
1318
|
-
response.data = {}
|
1319
|
-
end
|
1320
|
-
|
1321
|
-
end
|
1322
|
-
|
1323
|
-
# @overload abort_multipart_upload(options = {})
|
1324
|
-
# @param [Hash] options
|
1325
|
-
# @option options [required,String] :bucket_name
|
1326
|
-
# @option options [required,String] :key
|
1327
|
-
# @option options [required,String] :upload_id
|
1328
|
-
# @return [Core::Response]
|
1329
|
-
object_method(:abort_multipart_upload, :delete) do
|
1330
|
-
configure_request do |req, options|
|
1331
|
-
require_upload_id!(options[:upload_id])
|
1332
|
-
super(req, options)
|
1333
|
-
req.add_param('uploadId', options[:upload_id])
|
1334
|
-
end
|
1335
|
-
end
|
1336
|
-
|
1337
|
-
# @overload list_parts(options = {})
|
1338
|
-
# @param [Hash] options
|
1339
|
-
# @option options [required,String] :bucket_name
|
1340
|
-
# @option options [required,String] :key
|
1341
|
-
# @option options [required,String] :upload_id
|
1342
|
-
# @option options [Integer] :max_parts
|
1343
|
-
# @option options [Integer] :part_number_marker
|
1344
|
-
# @return [Core::Response]
|
1345
|
-
object_method(:list_parts, :get, XML::ListParts) do
|
1346
|
-
|
1347
|
-
configure_request do |req, options|
|
1348
|
-
require_upload_id!(options[:upload_id])
|
1349
|
-
super(req, options)
|
1350
|
-
req.add_param('uploadId', options[:upload_id])
|
1351
|
-
req.add_param('max-parts', options[:max_parts])
|
1352
|
-
req.add_param('part-number-marker', options[:part_number_marker])
|
1353
|
-
end
|
1354
|
-
|
1355
|
-
end
|
1356
|
-
|
1357
|
-
# Copies an object from one key to another.
|
1358
|
-
# @overload copy_object(options = {})
|
1359
|
-
# @param [Hash] options
|
1360
|
-
# @option options [required, String] :bucket_name Name of the bucket
|
1361
|
-
# to copy a object into.
|
1362
|
-
# @option options [required, String] :key Where (object key) in the
|
1363
|
-
# bucket the object should be copied to.
|
1364
|
-
# @option options [String] :website_redirect_location If the bucket is
|
1365
|
-
# configured as a website, redirects requests for this object to
|
1366
|
-
# another object in the same bucket or to an external URL.
|
1367
|
-
# @option options [required, String] :copy_source The source
|
1368
|
-
# bucket name and key, joined by a forward slash ('/').
|
1369
|
-
# This string must be URL-encoded. Additionally, you must
|
1370
|
-
# have read access to the source object.
|
1371
|
-
# @option options [String] :acl A canned ACL (e.g. 'private',
|
1372
|
-
# 'public-read', etc). See the S3 API documentation for
|
1373
|
-
# a complete list of valid values.
|
1374
|
-
# @option options [Symbol,String] :server_side_encryption (nil) The
|
1375
|
-
# algorithm used to encrypt the object on the server side
|
1376
|
-
# (e.g. :aes256).
|
1377
|
-
# @option options [String] :storage_class+ ('STANDARD')
|
1378
|
-
# Controls whether Reduced Redundancy Storage is enabled for
|
1379
|
-
# the object. Valid values are 'STANDARD' and
|
1380
|
-
# 'REDUCED_REDUNDANCY'.
|
1381
|
-
# @option options [String] :expires The date and time at which the
|
1382
|
-
# object is no longer cacheable.
|
1383
|
-
# @option options [String] :grant_read
|
1384
|
-
# @option options [String] :grant_write
|
1385
|
-
# @option options [String] :grant_read_acp
|
1386
|
-
# @option options [String] :grant_write_acp
|
1387
|
-
# @option options [String] :grant_full_control
|
1388
|
-
# @return [Core::Response]
|
1389
|
-
object_method(:copy_object, :put, :header_options => {
|
1390
|
-
:website_redirect_location => 'x-amz-website-redirect-location',
|
1391
|
-
:acl => 'x-amz-acl',
|
1392
|
-
:grant_read => 'x-amz-grant-read',
|
1393
|
-
:grant_write => 'x-amz-grant-write',
|
1394
|
-
:grant_read_acp => 'x-amz-grant-read-acp',
|
1395
|
-
:grant_write_acp => 'x-amz-grant-write-acp',
|
1396
|
-
:grant_full_control => 'x-amz-grant-full-control',
|
1397
|
-
:copy_source => 'x-amz-copy-source',
|
1398
|
-
:cache_control => 'Cache-Control',
|
1399
|
-
:metadata_directive => 'x-amz-metadata-directive',
|
1400
|
-
:content_type => 'Content-Type',
|
1401
|
-
:content_disposition => 'Content-Disposition',
|
1402
|
-
:expires => 'Expires',
|
1403
|
-
}) do
|
1404
|
-
|
1405
|
-
configure_request do |req, options|
|
1406
|
-
|
1407
|
-
validate!(:copy_source, options[:copy_source]) do
|
1408
|
-
"may not be blank" if options[:copy_source].to_s.empty?
|
1409
|
-
end
|
1410
|
-
|
1411
|
-
options = options.merge(:copy_source => escape_path(options[:copy_source]))
|
1412
|
-
super(req, options)
|
1413
|
-
req.metadata = options[:metadata]
|
1414
|
-
req.storage_class = options[:storage_class]
|
1415
|
-
req.server_side_encryption = options[:server_side_encryption]
|
1416
|
-
|
1417
|
-
if options[:version_id]
|
1418
|
-
req.headers['x-amz-copy-source'] += "?versionId=#{options[:version_id]}"
|
1419
|
-
end
|
1420
|
-
end
|
1421
|
-
|
1422
|
-
process_response do |resp|
|
1423
|
-
extract_object_headers(resp)
|
1424
|
-
end
|
1425
|
-
|
1426
|
-
end
|
1427
|
-
|
1428
|
-
object_method(:copy_part, :put, XML::CopyPart, :header_options => {
|
1429
|
-
:copy_source => 'x-amz-copy-source',
|
1430
|
-
:copy_source_range => 'x-amz-copy-source-range',
|
1431
|
-
}) do
|
1432
|
-
|
1433
|
-
configure_request do |request, options|
|
1434
|
-
|
1435
|
-
validate!(:copy_source, options[:copy_source]) do
|
1436
|
-
"may not be blank" if options[:copy_source].to_s.empty?
|
1437
|
-
end
|
1438
|
-
|
1439
|
-
validate!(:copy_source_range, options[:copy_source_range]) do
|
1440
|
-
"must start with bytes=" if options[:copy_source_range] && !options[:copy_source_range].start_with?("bytes=")
|
1441
|
-
end
|
1442
|
-
|
1443
|
-
options = options.merge(:copy_source => escape_path(options[:copy_source]))
|
1444
|
-
|
1445
|
-
require_upload_id!(options[:upload_id])
|
1446
|
-
request.add_param('uploadId', options[:upload_id])
|
1447
|
-
|
1448
|
-
require_part_number!(options[:part_number])
|
1449
|
-
request.add_param('partNumber', options[:part_number])
|
1450
|
-
|
1451
|
-
super(request, options)
|
1452
|
-
|
1453
|
-
if options[:version_id]
|
1454
|
-
req.headers['x-amz-copy-source'] += "?versionId=#{options[:version_id]}"
|
1455
|
-
end
|
1456
|
-
|
1457
|
-
end
|
1458
|
-
|
1459
|
-
end
|
1460
|
-
|
1461
|
-
protected
|
1462
|
-
|
1463
|
-
def extract_error_details response
|
1464
|
-
if
|
1465
|
-
(response.http_response.status >= 300 ||
|
1466
|
-
response.request_type == :complete_multipart_upload) and
|
1467
|
-
body = response.http_response.body and
|
1468
|
-
error = Core::XML::Parser.parse(body) and
|
1469
|
-
error[:code]
|
1470
|
-
then
|
1471
|
-
[error[:code], error[:message]]
|
1472
|
-
end
|
1473
|
-
end
|
1474
|
-
|
1475
|
-
def empty_response_body? response_body
|
1476
|
-
response_body.nil? or response_body == ''
|
1477
|
-
end
|
1478
|
-
|
1479
|
-
# There are a few of s3 requests that can generate empty bodies and
|
1480
|
-
# yet still be errors. These return empty bodies to comply with the
|
1481
|
-
# HTTP spec. We have to detect these errors specially.
|
1482
|
-
def populate_error resp
|
1483
|
-
code = resp.http_response.status
|
1484
|
-
if EMPTY_BODY_ERRORS.include?(code) and empty_response_body?(resp.http_response.body)
|
1485
|
-
error_class = EMPTY_BODY_ERRORS[code]
|
1486
|
-
resp.error = error_class.new(resp.http_request, resp.http_response)
|
1487
|
-
else
|
1488
|
-
super
|
1489
|
-
end
|
1490
|
-
end
|
1491
|
-
|
1492
|
-
def retryable_error? response
|
1493
|
-
super or
|
1494
|
-
failed_multipart_upload?(response) or
|
1495
|
-
response.error.is_a?(Errors::RequestTimeout)
|
1496
|
-
end
|
1497
|
-
|
1498
|
-
# S3 may return a 200 response code in response to complete_multipart_upload
|
1499
|
-
# and then start streaming whitespace until it knows the final result.
|
1500
|
-
# At that time it sends an XML message with success or failure.
|
1501
|
-
def failed_multipart_upload? response
|
1502
|
-
response.request_type == :complete_multipart_upload &&
|
1503
|
-
extract_error_details(response)
|
1504
|
-
end
|
1505
|
-
|
1506
|
-
def new_request
|
1507
|
-
req = S3::Request.new
|
1508
|
-
req.force_path_style = config.s3_force_path_style?
|
1509
|
-
req
|
1510
|
-
end
|
1511
|
-
|
1512
|
-
# Previously the access control policy could be specified via :acl
|
1513
|
-
# as a string or an object that responds to #to_xml. The prefered
|
1514
|
-
# method now is to pass :access_control_policy an xml document.
|
1515
|
-
def move_access_control_policy options
|
1516
|
-
if acl = options[:acl]
|
1517
|
-
if acl.is_a?(String) and is_xml?(acl)
|
1518
|
-
options[:access_control_policy] = options.delete(:acl)
|
1519
|
-
elsif acl.respond_to?(:to_xml)
|
1520
|
-
options[:access_control_policy] = options.delete(:acl).to_xml
|
1521
|
-
end
|
1522
|
-
end
|
1523
|
-
end
|
1524
|
-
|
1525
|
-
# @param [String] possible_xml
|
1526
|
-
# @return [Boolean] Returns `true` if the given string is a valid xml
|
1527
|
-
# document.
|
1528
|
-
def is_xml? possible_xml
|
1529
|
-
begin
|
1530
|
-
REXML::Document.new(possible_xml).has_elements?
|
1531
|
-
rescue
|
1532
|
-
false
|
1533
|
-
end
|
1534
|
-
end
|
1535
|
-
|
1536
|
-
def md5 str
|
1537
|
-
Base64.encode64(Digest::MD5.digest(str)).strip
|
1538
|
-
end
|
1539
|
-
|
1540
|
-
def parse_copy_part_response resp
|
1541
|
-
doc = REXML::Document.new(resp.http_response.body)
|
1542
|
-
resp[:etag] = doc.root.elements["ETag"].text
|
1543
|
-
resp[:last_modified] = doc.root.elements["LastModified"].text
|
1544
|
-
if header = resp.http_response.headers['x-amzn-requestid']
|
1545
|
-
data[:request_id] = [header].flatten.first
|
1546
|
-
end
|
1547
|
-
end
|
1548
|
-
|
1549
|
-
def extract_object_headers resp
|
1550
|
-
meta = {}
|
1551
|
-
resp.http_response.headers.each_pair do |name,value|
|
1552
|
-
if name =~ /^x-amz-meta-(.+)$/i
|
1553
|
-
meta[$1] = [value].flatten.join
|
1554
|
-
end
|
1555
|
-
end
|
1556
|
-
resp.data[:meta] = meta
|
1557
|
-
|
1558
|
-
if expiry = resp.http_response.headers['x-amz-expiration']
|
1559
|
-
expiry.first =~ /^expiry-date="(.+)", rule-id="(.+)"$/
|
1560
|
-
exp_date = DateTime.parse($1)
|
1561
|
-
exp_rule_id = $2
|
1562
|
-
else
|
1563
|
-
exp_date = nil
|
1564
|
-
exp_rule_id = nil
|
1565
|
-
end
|
1566
|
-
resp.data[:expiration_date] = exp_date if exp_date
|
1567
|
-
resp.data[:expiration_rule_id] = exp_rule_id if exp_rule_id
|
1568
|
-
|
1569
|
-
restoring = false
|
1570
|
-
restore_date = nil
|
1571
|
-
|
1572
|
-
if restore = resp.http_response.headers['x-amz-restore']
|
1573
|
-
if restore.first =~ /ongoing-request="(.+?)", expiry-date="(.+?)"/
|
1574
|
-
restoring = $1 == "true"
|
1575
|
-
restore_date = $2 && DateTime.parse($2)
|
1576
|
-
elsif restore.first =~ /ongoing-request="(.+?)"/
|
1577
|
-
restoring = $1 == "true"
|
1578
|
-
end
|
1579
|
-
end
|
1580
|
-
resp.data[:restore_in_progress] = restoring
|
1581
|
-
resp.data[:restore_expiration_date] = restore_date if restore_date
|
1582
|
-
|
1583
|
-
{
|
1584
|
-
'x-amz-version-id' => :version_id,
|
1585
|
-
'content-type' => :content_type,
|
1586
|
-
'content-encoding' => :content_encoding,
|
1587
|
-
'cache-control' => :cache_control,
|
1588
|
-
'expires' => :expires,
|
1589
|
-
'etag' => :etag,
|
1590
|
-
'x-amz-website-redirect-location' => :website_redirect_location,
|
1591
|
-
'accept-ranges' => :accept_ranges,
|
1592
|
-
}.each_pair do |header,method|
|
1593
|
-
if value = resp.http_response.header(header)
|
1594
|
-
resp.data[method] = value
|
1595
|
-
end
|
1596
|
-
end
|
1597
|
-
|
1598
|
-
if time = resp.http_response.header('Last-Modified')
|
1599
|
-
resp.data[:last_modified] = Time.parse(time)
|
1600
|
-
end
|
1601
|
-
|
1602
|
-
if length = resp.http_response.header('content-length')
|
1603
|
-
resp.data[:content_length] = length.to_i
|
1604
|
-
end
|
1605
|
-
|
1606
|
-
if sse = resp.http_response.header('x-amz-server-side-encryption')
|
1607
|
-
resp.data[:server_side_encryption] = sse.downcase.to_sym
|
1608
|
-
end
|
1609
|
-
|
1610
|
-
end
|
1611
|
-
|
1612
|
-
module Validators
|
1613
|
-
|
1614
|
-
# @return [Boolean] Returns true if the given bucket name is valid.
|
1615
|
-
def valid_bucket_name?(bucket_name)
|
1616
|
-
validate_bucket_name!(bucket_name) rescue false
|
1617
|
-
end
|
1618
|
-
|
1619
|
-
# Returns true if the given `bucket_name` is DNS compatible.
|
1620
|
-
#
|
1621
|
-
# DNS compatible bucket names may be accessed like:
|
1622
|
-
#
|
1623
|
-
# http://dns.compat.bucket.name.s3.amazonaws.com/
|
1624
|
-
#
|
1625
|
-
# Whereas non-dns compatible bucket names must place the bucket
|
1626
|
-
# name in the url path, like:
|
1627
|
-
#
|
1628
|
-
# http://s3.amazonaws.com/dns_incompat_bucket_name/
|
1629
|
-
#
|
1630
|
-
# @return [Boolean] Returns true if the given bucket name may be
|
1631
|
-
# is dns compatible.
|
1632
|
-
# this bucket n
|
1633
|
-
#
|
1634
|
-
def dns_compatible_bucket_name?(bucket_name)
|
1635
|
-
return false if
|
1636
|
-
!valid_bucket_name?(bucket_name) or
|
1637
|
-
|
1638
|
-
# Bucket names should be between 3 and 63 characters long
|
1639
|
-
bucket_name.size > 63 or
|
1640
|
-
|
1641
|
-
# Bucket names must only contain lowercase letters, numbers, dots, and dashes
|
1642
|
-
# and must start and end with a lowercase letter or a number
|
1643
|
-
bucket_name !~ /^[a-z0-9][a-z0-9.-]+[a-z0-9]$/ or
|
1644
|
-
|
1645
|
-
# Bucket names should not be formatted like an IP address (e.g., 192.168.5.4)
|
1646
|
-
bucket_name =~ /(\d+\.){3}\d+/ or
|
1647
|
-
|
1648
|
-
# Bucket names cannot contain two, adjacent periods
|
1649
|
-
bucket_name['..'] or
|
1650
|
-
|
1651
|
-
# Bucket names cannot contain dashes next to periods
|
1652
|
-
# (e.g., "my-.bucket.com" and "my.-bucket" are invalid)
|
1653
|
-
(bucket_name['-.'] || bucket_name['.-'])
|
1654
|
-
|
1655
|
-
true
|
1656
|
-
end
|
1657
|
-
|
1658
|
-
# Returns true if the bucket name must be used in the request
|
1659
|
-
# path instead of as a sub-domain when making requests against
|
1660
|
-
# S3.
|
1661
|
-
#
|
1662
|
-
# This can be an issue if the bucket name is DNS compatible but
|
1663
|
-
# contains '.' (periods). These cause the SSL certificate to
|
1664
|
-
# become invalid when making authenticated requets over SSL to the
|
1665
|
-
# bucket name. The solution is to send this as a path argument
|
1666
|
-
# instead.
|
1667
|
-
#
|
1668
|
-
# @return [Boolean] Returns true if the bucket name should be used
|
1669
|
-
# as a path segement instead of dns prefix when making requests
|
1670
|
-
# against s3.
|
1671
|
-
#
|
1672
|
-
def path_style_bucket_name? bucket_name
|
1673
|
-
if dns_compatible_bucket_name?(bucket_name)
|
1674
|
-
bucket_name =~ /\./ ? true : false
|
1675
|
-
else
|
1676
|
-
true
|
1677
|
-
end
|
1678
|
-
end
|
1679
|
-
|
1680
|
-
def validate! name, value, &block
|
1681
|
-
if error_msg = yield
|
1682
|
-
raise ArgumentError, "#{name} #{error_msg}"
|
1683
|
-
end
|
1684
|
-
value
|
1685
|
-
end
|
1686
|
-
|
1687
|
-
def validate_key!(key)
|
1688
|
-
validate!('key', key) do
|
1689
|
-
case
|
1690
|
-
when key.nil? || key == ''
|
1691
|
-
'may not be blank'
|
1692
|
-
end
|
1693
|
-
end
|
1629
|
+
process_response do |resp|
|
1630
|
+
extract_object_headers(resp)
|
1694
1631
|
end
|
1695
1632
|
|
1696
|
-
|
1697
|
-
|
1698
|
-
raise ArgumentError, "bucket_name may not be blank"
|
1699
|
-
end
|
1633
|
+
simulate_response do |response|
|
1634
|
+
response.data[:etag] = 'abc123'
|
1700
1635
|
end
|
1636
|
+
end
|
1701
1637
|
|
1702
|
-
|
1703
|
-
|
1704
|
-
|
1705
|
-
|
1706
|
-
|
1707
|
-
|
1708
|
-
|
1709
|
-
|
1710
|
-
|
1711
|
-
|
1712
|
-
|
1713
|
-
|
1714
|
-
|
1715
|
-
|
1638
|
+
# @overload complete_multipart_upload(options = {})
|
1639
|
+
# @param [Hash] options
|
1640
|
+
# @option options [required,String] :bucket_name
|
1641
|
+
# @option options [required,String] :key
|
1642
|
+
# @option options [required,String] :upload_id
|
1643
|
+
# @option options [required,Array<Hash>] :parts An array of hashes
|
1644
|
+
# with the following keys:
|
1645
|
+
# * `:part_number` [Integer] - *required*
|
1646
|
+
# * `:etag` [String] - *required*
|
1647
|
+
# @return [Core::Response]
|
1648
|
+
object_method(:complete_multipart_upload, :post,
|
1649
|
+
XML::CompleteMultipartUpload) do
|
1650
|
+
configure_request do |req, options|
|
1651
|
+
require_upload_id!(options[:upload_id])
|
1652
|
+
validate_parts!(options[:parts])
|
1653
|
+
super(req, options)
|
1654
|
+
req.add_param('uploadId', options[:upload_id])
|
1655
|
+
|
1656
|
+
req.body = Nokogiri::XML::Builder.new do |xml|
|
1657
|
+
xml.CompleteMultipartUpload do
|
1658
|
+
options[:parts].each do |part|
|
1659
|
+
xml.Part do
|
1660
|
+
xml.PartNumber(part[:part_number])
|
1661
|
+
xml.ETag(part[:etag])
|
1662
|
+
end
|
1663
|
+
end
|
1716
1664
|
end
|
1717
|
-
end
|
1665
|
+
end.doc.root.to_xml
|
1666
|
+
|
1718
1667
|
end
|
1719
1668
|
|
1720
|
-
|
1721
|
-
|
1722
|
-
case
|
1723
|
-
when policy.nil? || policy == ''
|
1724
|
-
'may not be blank'
|
1725
|
-
else
|
1726
|
-
json_validation_message(policy)
|
1727
|
-
end
|
1728
|
-
end
|
1669
|
+
process_response do |resp|
|
1670
|
+
extract_object_headers(resp)
|
1729
1671
|
end
|
1730
1672
|
|
1731
|
-
|
1732
|
-
|
1733
|
-
:acl,
|
1734
|
-
:grant_read,
|
1735
|
-
:grant_write,
|
1736
|
-
:grant_read_acp,
|
1737
|
-
:grant_write_acp,
|
1738
|
-
:grant_full_control,
|
1739
|
-
:access_control_policy,
|
1740
|
-
]
|
1741
|
-
unless options.keys.any?{|opt| acl_options.include?(opt) }
|
1742
|
-
msg = "missing a required ACL option, must provide an ACL " +
|
1743
|
-
"via :acl, :grant_* or :access_control_policy"
|
1744
|
-
raise ArgumentError, msg
|
1745
|
-
end
|
1673
|
+
simulate_response do |response|
|
1674
|
+
response.data = {}
|
1746
1675
|
end
|
1747
1676
|
|
1748
|
-
|
1677
|
+
end
|
1749
1678
|
|
1750
|
-
|
1751
|
-
|
1752
|
-
|
1753
|
-
|
1754
|
-
|
1755
|
-
|
1679
|
+
# @overload abort_multipart_upload(options = {})
|
1680
|
+
# @param [Hash] options
|
1681
|
+
# @option options [required,String] :bucket_name
|
1682
|
+
# @option options [required,String] :key
|
1683
|
+
# @option options [required,String] :upload_id
|
1684
|
+
# @return [Core::Response]
|
1685
|
+
object_method(:abort_multipart_upload, :delete) do
|
1686
|
+
configure_request do |req, options|
|
1687
|
+
require_upload_id!(options[:upload_id])
|
1688
|
+
super(req, options)
|
1689
|
+
req.add_param('uploadId', options[:upload_id])
|
1690
|
+
end
|
1691
|
+
end
|
1756
1692
|
|
1757
|
-
|
1758
|
-
|
1693
|
+
# @overload list_parts(options = {})
|
1694
|
+
# @param [Hash] options
|
1695
|
+
# @option options [required,String] :bucket_name
|
1696
|
+
# @option options [required,String] :key
|
1697
|
+
# @option options [required,String] :upload_id
|
1698
|
+
# @option options [Integer] :max_parts
|
1699
|
+
# @option options [Integer] :part_number_marker
|
1700
|
+
# @return [Core::Response]
|
1701
|
+
object_method(:list_parts, :get, XML::ListParts) do
|
1759
1702
|
|
1703
|
+
configure_request do |req, options|
|
1704
|
+
require_upload_id!(options[:upload_id])
|
1705
|
+
super(req, options)
|
1706
|
+
req.add_param('uploadId', options[:upload_id])
|
1707
|
+
req.add_param('max-parts', options[:max_parts])
|
1708
|
+
req.add_param('part-number-marker', options[:part_number_marker])
|
1760
1709
|
end
|
1761
1710
|
|
1762
|
-
|
1763
|
-
|
1764
|
-
|
1711
|
+
end
|
1712
|
+
|
1713
|
+
# Copies an object from one key to another.
|
1714
|
+
# @overload copy_object(options = {})
|
1715
|
+
# @param [Hash] options
|
1716
|
+
# @option options [required, String] :bucket_name Name of the bucket
|
1717
|
+
# to copy a object into.
|
1718
|
+
# @option options [required, String] :key Where (object key) in the
|
1719
|
+
# bucket the object should be copied to.
|
1720
|
+
# @option options [String] :website_redirect_location If the bucket is
|
1721
|
+
# configured as a website, redirects requests for this object to
|
1722
|
+
# another object in the same bucket or to an external URL.
|
1723
|
+
# @option options [required, String] :copy_source The source
|
1724
|
+
# bucket name and key, joined by a forward slash ('/').
|
1725
|
+
# This string must be URL-encoded. Additionally, you must
|
1726
|
+
# have read access to the source object.
|
1727
|
+
# @option options [String] :acl A canned ACL (e.g. 'private',
|
1728
|
+
# 'public-read', etc). See the S3 API documentation for
|
1729
|
+
# a complete list of valid values.
|
1730
|
+
# @option options [Symbol,String] :server_side_encryption (nil) The
|
1731
|
+
# algorithm used to encrypt the object on the server side
|
1732
|
+
# (e.g. :aes256).
|
1733
|
+
# @option options [String] :storage_class+ ('STANDARD')
|
1734
|
+
# Controls whether Reduced Redundancy Storage is enabled for
|
1735
|
+
# the object. Valid values are 'STANDARD' and
|
1736
|
+
# 'REDUCED_REDUNDANCY'.
|
1737
|
+
# @option options [String] :expires The date and time at which the
|
1738
|
+
# object is no longer cacheable.
|
1739
|
+
# @option options [String] :grant_read
|
1740
|
+
# @option options [String] :grant_write
|
1741
|
+
# @option options [String] :grant_read_acp
|
1742
|
+
# @option options [String] :grant_write_acp
|
1743
|
+
# @option options [String] :grant_full_control
|
1744
|
+
# @return [Core::Response]
|
1745
|
+
object_method(:copy_object, :put, :header_options => {
|
1746
|
+
:website_redirect_location => 'x-amz-website-redirect-location',
|
1747
|
+
:acl => 'x-amz-acl',
|
1748
|
+
:grant_read => 'x-amz-grant-read',
|
1749
|
+
:grant_write => 'x-amz-grant-write',
|
1750
|
+
:grant_read_acp => 'x-amz-grant-read-acp',
|
1751
|
+
:grant_write_acp => 'x-amz-grant-write-acp',
|
1752
|
+
:grant_full_control => 'x-amz-grant-full-control',
|
1753
|
+
:copy_source => 'x-amz-copy-source',
|
1754
|
+
:cache_control => 'Cache-Control',
|
1755
|
+
:metadata_directive => 'x-amz-metadata-directive',
|
1756
|
+
:content_type => 'Content-Type',
|
1757
|
+
:content_disposition => 'Content-Disposition',
|
1758
|
+
:expires => 'Expires',
|
1759
|
+
}) do
|
1760
|
+
|
1761
|
+
configure_request do |req, options|
|
1762
|
+
|
1763
|
+
validate!(:copy_source, options[:copy_source]) do
|
1764
|
+
"may not be blank" if options[:copy_source].to_s.empty?
|
1765
1765
|
end
|
1766
|
-
end
|
1767
1766
|
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1767
|
+
options = options.merge(:copy_source => escape_path(options[:copy_source]))
|
1768
|
+
super(req, options)
|
1769
|
+
req.metadata = options[:metadata]
|
1770
|
+
req.storage_class = options[:storage_class]
|
1771
|
+
req.server_side_encryption = options[:server_side_encryption]
|
1772
|
+
|
1773
|
+
if options[:version_id]
|
1774
|
+
req.headers['x-amz-copy-source'] += "?versionId=#{options[:version_id]}"
|
1771
1775
|
end
|
1772
1776
|
end
|
1773
1777
|
|
1774
|
-
|
1775
|
-
|
1776
|
-
if !parts.kind_of?(Array)
|
1777
|
-
"must not be blank"
|
1778
|
-
elsif parts.empty?
|
1779
|
-
"must contain at least one entry"
|
1780
|
-
elsif !parts.all? { |p| p.kind_of?(Hash) }
|
1781
|
-
"must be an array of hashes"
|
1782
|
-
elsif !parts.all? { |p| p[:part_number] }
|
1783
|
-
"must contain part_number for each part"
|
1784
|
-
elsif !parts.all? { |p| p[:etag] }
|
1785
|
-
"must contain etag for each part"
|
1786
|
-
elsif parts.any? { |p| p[:part_number].to_i < 1 }
|
1787
|
-
"must not have part numbers less than 1"
|
1788
|
-
end
|
1789
|
-
end
|
1778
|
+
process_response do |resp|
|
1779
|
+
extract_object_headers(resp)
|
1790
1780
|
end
|
1791
1781
|
|
1792
|
-
|
1793
|
-
|
1794
|
-
|
1795
|
-
|
1796
|
-
|
1782
|
+
end
|
1783
|
+
|
1784
|
+
object_method(:copy_part, :put, XML::CopyPart, :header_options => {
|
1785
|
+
:copy_source => 'x-amz-copy-source',
|
1786
|
+
:copy_source_range => 'x-amz-copy-source-range',
|
1787
|
+
}) do
|
1788
|
+
|
1789
|
+
configure_request do |request, options|
|
1790
|
+
|
1791
|
+
validate!(:copy_source, options[:copy_source]) do
|
1792
|
+
"may not be blank" if options[:copy_source].to_s.empty?
|
1797
1793
|
end
|
1798
1794
|
|
1799
|
-
|
1800
|
-
|
1801
|
-
JSON.parse(obj)
|
1802
|
-
rescue => e
|
1803
|
-
error = e
|
1795
|
+
validate!(:copy_source_range, options[:copy_source_range]) do
|
1796
|
+
"must start with bytes=" if options[:copy_source_range] && !options[:copy_source_range].start_with?("bytes=")
|
1804
1797
|
end
|
1805
|
-
"contains invalid JSON: #{error}" if error
|
1806
|
-
end
|
1807
1798
|
|
1808
|
-
|
1799
|
+
options = options.merge(:copy_source => escape_path(options[:copy_source]))
|
1809
1800
|
|
1810
|
-
|
1811
|
-
|
1801
|
+
require_upload_id!(options[:upload_id])
|
1802
|
+
request.add_param('uploadId', options[:upload_id])
|
1812
1803
|
|
1813
|
-
|
1804
|
+
require_part_number!(options[:part_number])
|
1805
|
+
request.add_param('partNumber', options[:part_number])
|
1806
|
+
|
1807
|
+
super(request, options)
|
1814
1808
|
|
1815
|
-
|
1809
|
+
if options[:version_id]
|
1810
|
+
req.headers['x-amz-copy-source'] += "?versionId=#{options[:version_id]}"
|
1811
|
+
end
|
1812
|
+
|
1813
|
+
end
|
1814
|
+
|
1815
|
+
end
|
1816
1816
|
|
1817
|
+
end
|
1817
1818
|
end
|
1818
1819
|
end
|