right_aws_api 0.1.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 (37) hide show
  1. checksums.yaml +7 -0
  2. data/HISTORY +2 -0
  3. data/LICENSE +19 -0
  4. data/README.md +164 -0
  5. data/Rakefile +38 -0
  6. data/lib/cloud/aws/as/manager.rb +118 -0
  7. data/lib/cloud/aws/base/helpers/utils.rb +328 -0
  8. data/lib/cloud/aws/base/manager.rb +186 -0
  9. data/lib/cloud/aws/base/parsers/response_error.rb +117 -0
  10. data/lib/cloud/aws/base/routines/request_signer.rb +80 -0
  11. data/lib/cloud/aws/cf/manager.rb +171 -0
  12. data/lib/cloud/aws/cf/routines/request_signer.rb +70 -0
  13. data/lib/cloud/aws/cf/wrappers/default.rb +213 -0
  14. data/lib/cloud/aws/cfm/manager.rb +90 -0
  15. data/lib/cloud/aws/cw/manager.rb +113 -0
  16. data/lib/cloud/aws/eb/manager.rb +87 -0
  17. data/lib/cloud/aws/ec/manager.rb +91 -0
  18. data/lib/cloud/aws/ec2/manager.rb +222 -0
  19. data/lib/cloud/aws/elb/manager.rb +120 -0
  20. data/lib/cloud/aws/emr/manager.rb +86 -0
  21. data/lib/cloud/aws/iam/manager.rb +100 -0
  22. data/lib/cloud/aws/rds/manager.rb +110 -0
  23. data/lib/cloud/aws/route53/manager.rb +177 -0
  24. data/lib/cloud/aws/route53/routines/request_signer.rb +70 -0
  25. data/lib/cloud/aws/route53/wrappers/default.rb +132 -0
  26. data/lib/cloud/aws/s3/manager.rb +373 -0
  27. data/lib/cloud/aws/s3/parsers/response_error.rb +76 -0
  28. data/lib/cloud/aws/s3/routines/request_signer.rb +243 -0
  29. data/lib/cloud/aws/s3/wrappers/default.rb +315 -0
  30. data/lib/cloud/aws/sdb/manager.rb +150 -0
  31. data/lib/cloud/aws/sns/manager.rb +96 -0
  32. data/lib/cloud/aws/sqs/manager.rb +335 -0
  33. data/lib/right_aws_api.rb +45 -0
  34. data/lib/right_aws_api_version.rb +40 -0
  35. data/right_aws_api.gemspec +55 -0
  36. data/spec/describe_calls.rb +92 -0
  37. metadata +118 -0
@@ -0,0 +1,76 @@
1
+ #--
2
+ # Copyright (c) 2013 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ module RightScale
25
+ module CloudApi
26
+ module Parser
27
+ module AWS
28
+
29
+ # S3 Parsers namespace
30
+ module S3
31
+
32
+ # S3 response error
33
+ class ResponseError
34
+
35
+ # Parses HTTP error message from a response body
36
+ #
37
+ # @param [RightScale::CloudApi::HttpResponse] response
38
+ # @param [Hash] options
39
+ # @option options [Object] :xml_parser
40
+ #
41
+ # @return [String] to be displayed/logged.
42
+ #
43
+ # @example
44
+ # {"Error"=>
45
+ # {"Message"=>"The specified key does not exist.",
46
+ # "RequestId"=>"B9BE7751749FA764",
47
+ # "Code"=>"NoSuchKey",
48
+ # "HostId"=>
49
+ # "xtvFjUBrzKb6ndg3XTlGdAkGPm8KByqCzcLdK83fHi++ztDOC83Bv3+uH82DIBHj",
50
+ # "Key"=>"boot.jpg-"}}
51
+ #
52
+ def self.parse(response, options={})
53
+ body = response.body.to_s
54
+ result = "#{response.code}"
55
+
56
+ return result if body._blank?
57
+
58
+ is_xml = response['content-type'].to_s[/xml/] ||
59
+ body[/\A<\?xml /]
60
+
61
+ if is_xml
62
+ hash = Utils::get_xml_parser_class(options[:xml_parser]).parse(body)
63
+ error = hash["Error"]
64
+ result << (error ? ": #{error['Code']}: #{error['Message']} (RequestID: #{error['RequestId']})" : ": #{body}")
65
+ else
66
+ result << ": #{body}"
67
+ end
68
+ result
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,243 @@
1
+ #--
2
+ # Copyright (c) 2013 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ module RightScale
25
+ module CloudApi
26
+ module AWS
27
+ module S3
28
+
29
+ # S3 Request signer
30
+ class RequestSigner < CloudApi::Routine
31
+
32
+ # S3 Request signer Error
33
+ class Error < CloudApi::Error
34
+ end
35
+
36
+ # This guys are used to sign a request
37
+ SUB_RESOURCES = %w{
38
+ acl
39
+ cors
40
+ delete
41
+ lifecycle
42
+ location
43
+ logging
44
+ notification
45
+ policy
46
+ requestPayment
47
+ tagging
48
+ torrent
49
+ uploads
50
+ versionId
51
+ versioning
52
+ versions
53
+ website
54
+ }
55
+
56
+
57
+ # Using Query String API Amazon allows to override some of response headers:
58
+ #
59
+ # response-content-type response-content-language response-expires
60
+ # reponse-cache-control response-content-disposition response-content-encoding
61
+ #
62
+ # @see http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGET.html?r=2145
63
+ #
64
+ OVERRIDE_RESPONSE_HEADERS = /^response-/
65
+
66
+ # One year in seconds
67
+ ONE_YEAR_OF_SECONDS = 365*60*60*24
68
+
69
+ # Authenticates an S3 request
70
+ #
71
+ # @return [void]
72
+ #
73
+ # @example
74
+ # # no example
75
+ #
76
+ def process
77
+ # Extract sub-resource(s).
78
+ # Sub-resources are acl, torrent, versioning, location etc.
79
+ sub_resources = {}
80
+ @data[:request][:params].each do |key, value|
81
+ sub_resources[key] = (value._blank? ? nil : value) if SUB_RESOURCES.include?(key) || key[OVERRIDE_RESPONSE_HEADERS]
82
+ end
83
+
84
+ # Extract bucket name and object path
85
+ if @data[:request][:bucket]._blank?
86
+ # This is a very first attempt:
87
+ # 1. extract the bucket name from the path
88
+ # 2. save the bucket into request data vars
89
+ bucket_name, @data[:request][:relative_path] = @data[:request][:relative_path].to_s[/^([^\/]*)\/?(.*)$/] && [$1, $2]
90
+ @data[:request][:bucket] = bucket_name
91
+ # Static path is the path that the original URL has.
92
+ # 1. For Amazon it is always ''.
93
+ # 2. For Euca it is usually a non-blank string.
94
+ static_path = @data[:connection][:uri].path
95
+ # Add trailing '/' to the path unless it is.
96
+ static_path = "#{static_path}/" unless static_path[/\/$/]
97
+ # Store the path: we may need it for signing redirects later.
98
+ @data[:request][:static_path] = static_path
99
+ else
100
+ # This is a retry or a redirect:
101
+ # 1. Extract the bucket name from the request data;
102
+ # 2. Get rid of the path the remote server sent in the location header. We are
103
+ # re-signing the request and have to build everything from the scratch.
104
+ # In the crazy case when the new location has path differs from the original one
105
+ # we are screwed up and we will get "SignatureDoesNotMatch" error. But this does
106
+ # not seem to be the case for Amazon or Euca.
107
+ bucket_name = @data[:request][:bucket]
108
+ # Revert static path back to the original value.
109
+ static_path = @data[:request][:static_path]
110
+ @data[:connection][:uri].path = static_path
111
+ end
112
+
113
+ # Escape that part of the path that may have UTF-8 chars (in S3 Object name for instance).
114
+ # Amazon wants them to be escaped before we sign the request.
115
+ #
116
+ # P.S. Escape AFTER we extract bucket name.
117
+ @data[:request][:relative_path] = Utils::AWS::amz_escape(@data[:request][:relative_path])
118
+
119
+ # Calculate a canonical path (bucket part must end with '/')
120
+ bucket_string = Utils::AWS::is_dns_bucket?(bucket_name) ? "#{bucket_name}/" : bucket_name.to_s
121
+ canonicalized_path = Utils::join_urn(static_path,
122
+ bucket_string,
123
+ @data[:request][:relative_path],
124
+ sub_resources ){ |value| value } # pass this block to avoid escaping: Amazon does not like escaped things in canonicalized_path
125
+
126
+ # Make sure headers required for authentication are set
127
+ unless @data[:options][:cloud][:link]
128
+ # Make sure 'content-type' is set.
129
+ # P.S. Ruby 2.1+ sets 'content-type' by default for POST and PUT requests.
130
+ # So we need to include it into our signature to avoid the error below:
131
+ # 'The request signature we calculated does not match the signature you provided.
132
+ # Check your key and signing method.'
133
+ @data[:request][:headers].set_if_blank('content-type', 'application/octet-stream')
134
+ # REST Auth:
135
+ unless @data[:request][:body]._blank?
136
+ # Fix body if it is a Hash instance
137
+ if @data[:request][:body].is_a?(Hash)
138
+ @data[:request][:body] = Utils::contentify_body(@data[:request][:body], @data[:request][:headers]['content-type'])
139
+ end
140
+ # Calculate 'content-md5' when possible (some API calls wanna have it set)
141
+ if @data[:request][:body].is_a?(String)
142
+ @data[:request][:headers]['content-md5'] = Base64::encode64(Digest::MD5::digest(@data[:request][:body])).strip
143
+ end
144
+ end
145
+ # Set date
146
+ @data[:request][:headers].set_if_blank('date', Time::now.utc.httpdate)
147
+ # Sign a request
148
+ signature = Utils::AWS::sign_s3_signature( @data[:credentials][:aws_secret_access_key],
149
+ @data[:request][:verb],
150
+ canonicalized_path,
151
+ @data[:request][:headers])
152
+ @data[:request][:headers]['authorization'] = "AWS #{@data[:credentials][:aws_access_key_id]}:#{signature}"
153
+ else
154
+ # @see http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html
155
+ #
156
+ # Amazon: ... We assume that when a browser makes the GET request, it won't provide a Content-MD5 or a Content-Type header,
157
+ # nor will it set any x-amz- headers, so those parts of the StringToSign are left blank. ...
158
+ #
159
+ # Only GET requests!
160
+ raise Error::new("Only GET requests are supported by S3 Query String API") unless @data[:request][:verb] == :get
161
+ # Expires
162
+ expires = Utils::dearrayify(@data[:request][:headers]['expires'].first || (Time.now.utc.to_i + ONE_YEAR_OF_SECONDS))
163
+ expires = expires.to_i unless expires.is_a?(Fixnum)
164
+ # QUERY STRING AUTH: ('expires' and 'x-amz-*' headers are not supported)
165
+ @data[:request][:params]['Expires'] = expires
166
+ @data[:request][:headers]['expires'] = expires # a hack to sign a record
167
+ @data[:request][:headers].dup.each do |header, values|
168
+ @data[:request][:headers].delete(header) unless header.to_s[/(^x-amz-)|(^expires$)/]
169
+ end
170
+ @data[:request][:params]['AWSAccessKeyId'] = @data[:credentials][:aws_access_key_id]
171
+ # Sign a request
172
+ signature = Utils::AWS::sign_s3_signature( @data[:credentials][:aws_secret_access_key],
173
+ @data[:request][:verb],
174
+ canonicalized_path,
175
+ @data[:request][:headers] )
176
+ @data[:request][:params]['Signature'] = signature
177
+ # we dont need this header any more
178
+ @data[:request][:headers].delete('expires')
179
+ end
180
+
181
+ # Sub-domain compatible buckets vs incompatible ones
182
+ if !@data[:options][:cloud][:no_subdomains] && Utils::AWS::is_dns_bucket?(bucket_name)
183
+ # DNS compatible bucket name:
184
+ #
185
+ # Figure out if we need to add bucket name into the host name. It is rediculous but
186
+ # sometimes Amazon returns a redirect to a host with the bucket name already mixed in
187
+ # but sometimes without.
188
+ # The only thing we can do so far is to check if the host name starts with the bucket
189
+ # and the name is at least 4th level DNS name.
190
+ #
191
+ # Examples:
192
+ # * my-bucket.s3-ap-southeast-2.amazonaws.com
193
+ # * my-bucket.s3.amazonaws.com
194
+ # * s3.amazonaws.com
195
+ #
196
+ # P.S. This assumtion will not work for any other providers but we will figure it out later
197
+ # if we support any. The only other provider we support is Eucalyptus but it always
198
+ # expects that the bucket goes into path and never into the host therefore we are
199
+ # OK with Euca (Euca is expected to be run with :no_subdomains => true).
200
+ #
201
+ unless @data[:connection][:uri].host[/^#{bucket_name}\..+\.[^.]+\.[^.]+$/]
202
+ # If there was a redirect and it had 'location' header then there is nothing to do with the host
203
+ # otherwise we have to add the bucket to the host.
204
+ # P.S. When Amazon returns a redirect (usually 301) with the new host in the message body
205
+ # the new host does not have the bucket name in it. But if it is 307 and the host is in the location
206
+ # header then that host name already includes the bucket in it. Grrrr....
207
+ @data[:connection][:uri].host = "#{bucket_name}.#{@data[:connection][:uri].host}"
208
+ end
209
+ @data[:request][:path] = Utils::join_urn( @data[:connection][:uri].path,
210
+ @data[:request][:relative_path],
211
+ @data[:request][:params] )
212
+ else
213
+ # Old incompatible or Eucalyptus
214
+ @data[:request][:path] = Utils::join_urn( @data[:connection][:uri].path,
215
+ "#{bucket_name}",
216
+ @data[:request][:relative_path],
217
+ @data[:request][:params] )
218
+ end
219
+
220
+ # Host should be set for REST requests (and should not affect on Query String ones)
221
+ @data[:request][:headers]['host'] = @data[:connection][:uri].host
222
+
223
+ # Finalize data
224
+ if @data[:options][:cloud][:link]
225
+ # Amazon supports only some GET requests without body and any headers:
226
+ # Return the link
227
+ uri = @data[:connection][:uri].clone
228
+ uri.path, uri.query = @data[:request][:path].split('?')
229
+ @data[:result] = {
230
+ "verb" => @data[:request][:verb].to_s,
231
+ "link" => uri.to_s,
232
+ "headers" => @data[:request][:headers]
233
+ }
234
+ # Query Auth:we should stop here because we just generated a link for the third part usage
235
+ @data[:vars][:system][:done] = true
236
+ end
237
+ end
238
+ end
239
+
240
+ end
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,315 @@
1
+ #--
2
+ # Copyright (c) 2013 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ module RightScale
25
+ module CloudApi
26
+ module AWS
27
+ module S3
28
+
29
+ # S3 Wrapper namespace
30
+ module Wrapper
31
+
32
+ # Default wrapper
33
+ module DEFAULT
34
+
35
+
36
+ # Defines QUERY API like methods for the service
37
+ #
38
+ # @return [void]
39
+ #
40
+ # @example
41
+ # # no example
42
+ #
43
+ def self.extended(base)
44
+
45
+ #-----------------
46
+ # Service
47
+ #-----------------
48
+
49
+ base.query_api_pattern 'ListAllMyBuckets', :get
50
+
51
+
52
+ base.query_api_pattern 'ListBuckets', :get # alias for ListAllMyBuckets
53
+
54
+
55
+ #-----------------
56
+ # Bucket
57
+ #-----------------
58
+
59
+
60
+ base.query_api_pattern 'DeleteBucket', :delete, '{:Bucket}'
61
+
62
+
63
+ base.query_api_pattern 'DeleteBucketPolicy', :delete, '{:Bucket}',
64
+ :params => { 'policy' => nil }
65
+
66
+
67
+ base.query_api_pattern 'DeleteBucketWebsite', :delete, '{:Bucket}',
68
+ :params => { 'website' => nil }
69
+
70
+
71
+ base.query_api_pattern 'ListObjects', :get, '{:Bucket}'
72
+ base.query_api_pattern 'GetBucket', :get, '{:Bucket}' # alias for ListObjects
73
+
74
+
75
+ base.query_api_pattern 'GetBucketAcl', :get, '{:Bucket}',
76
+ :params => { 'acl'=> nil }
77
+
78
+
79
+ base.query_api_pattern 'GetBucketPolicy', :get, '{:Bucket}',
80
+ :params => { 'policy'=> nil }
81
+
82
+
83
+ base.query_api_pattern 'GetBucketLocation', :get, '{:Bucket}',
84
+ :params => { 'location'=> nil }
85
+
86
+
87
+ base.query_api_pattern 'GetBucketLogging', :get, '{:Bucket}',
88
+ :params => { 'logging'=> nil }
89
+
90
+
91
+ base.query_api_pattern 'GetBucketNotification', :get, '{:Bucket}',
92
+ :params => { 'notification'=> nil }
93
+
94
+
95
+ base.query_api_pattern 'GetBucketVersions', :get, '{:Bucket}',
96
+ :params => { 'versions'=> nil }
97
+
98
+
99
+ base.query_api_pattern 'GetBucketRequestPayment', :get, '{:Bucket}',
100
+ :params => { 'requestPayment'=> nil }
101
+
102
+
103
+ base.query_api_pattern 'GetBucketVersioning', :get, '{:Bucket}',
104
+ :params => { 'versioning'=> nil }
105
+
106
+
107
+ base.query_api_pattern 'GetBucketWebsite', :get, '{:Bucket}',
108
+ :params => { 'website'=> nil }
109
+
110
+
111
+ base.query_api_pattern 'ListMultipartUploads', :get, '{:Bucket}',
112
+ :params => { 'uploads'=> nil }
113
+
114
+
115
+ base.query_api_pattern 'PutBucket', :put, '{:Bucket}'
116
+
117
+
118
+ base.query_api_pattern 'PutBucketAcl', :put, '{:Bucket}',
119
+ :params => { 'acl' => nil },
120
+ :body => { 'AccessControlPolicy' => :AccessControlPolicy },
121
+ :headers => { 'content-type' => 'application/xml'}
122
+
123
+
124
+ base.query_api_pattern 'PutBucketPolicy', :put, '{:Bucket}',
125
+ :params => { 'policy' => nil },
126
+ :body => Utils::MUST_BE_SET,
127
+ :headers => { 'content-type' => 'application/json' }
128
+
129
+
130
+ base.query_api_pattern 'PutBucketLogging', :put, '{:Bucket}',
131
+ :params => { 'logging' => nil },
132
+ :body => { 'BucketLoggingStatus' => :BucketLoggingStatus },
133
+ :headers => { 'content-type' => 'application/xml'}
134
+
135
+
136
+ base.query_api_pattern 'PutBucketNotification', :put, '{:Bucket}',
137
+ :params => { 'notification' => nil },
138
+ :body => { 'NotificationConfiguration' => :NotificationConfiguration },
139
+ :headers => { 'content-type' => 'application/xml'}
140
+
141
+
142
+ base.query_api_pattern 'PutBucketRequestPayment', :put, '{:Bucket}',
143
+ :params => { 'requestPayment' => nil },
144
+ :body => { 'RequestPaymentConfiguration' => :RequestPaymentConfiguration },
145
+ :headers => { 'content-type' => 'application/xml'}
146
+
147
+
148
+ base.query_api_pattern 'PutBucketVersioning', :put, '{:Bucket}',
149
+ :params => { 'versioning' => nil },
150
+ :body => { 'VersioningConfiguration' => :VersioningConfiguration },
151
+ :headers => { 'content-type' => 'application/xml'}
152
+
153
+
154
+ base.query_api_pattern 'PutBucketWebsite', :put, '{:Bucket}',
155
+ :params => { 'website' => nil },
156
+ :body => { 'WebsiteConfiguration' => :WebsiteConfiguration },
157
+ :headers => { 'content-type' => 'application/xml'}
158
+
159
+
160
+ base.query_api_pattern 'GetBucketCors', :get, '{:Bucket}',
161
+ :params => { 'cors'=> nil }
162
+
163
+
164
+ base.query_api_pattern 'DeleteBucketCors', :delete, '{:Bucket}',
165
+ :params => { 'cors'=> nil }
166
+
167
+
168
+ base.query_api_pattern 'PutBucketCors', :put, '{:Bucket}',
169
+ :params => { 'cors'=> nil },
170
+ :body => { 'CORSConfiguration' => { 'CORSRule' => :CORSRule } },
171
+ :headers => { 'content-type' => 'application/xml'}
172
+
173
+
174
+ base.query_api_pattern 'GetBucketTagging', :get, '{:Bucket}',
175
+ :params => { 'tagging' => nil }
176
+
177
+
178
+ base.query_api_pattern 'PutBucketTagging', :put, '{:Bucket}',
179
+ :params => { 'tagging' => nil },
180
+ :body => { 'Tagging' => { 'TagSet' => { 'Tag' => :TagSet } } },
181
+ :headers => { 'content-type' => 'application/xml'}
182
+
183
+
184
+ base.query_api_pattern 'DeleteBucketTagging', :delete, '{:Bucket}',
185
+ :params => { 'tagging' => nil }
186
+
187
+
188
+ base.query_api_pattern 'GetBucketLifecycle', :get, '{:Bucket}',
189
+ :params => { 'lifecycle' => nil }
190
+
191
+
192
+ base.query_api_pattern 'PutBucketLifecycle', :put, '{:Bucket}',
193
+ :params => { 'lifecycle' => nil },
194
+ :body => { 'LifecycleConfiguration' => { 'Rule' => :Rule } },
195
+ :headers => { 'content-type' => 'application/xml'}
196
+
197
+
198
+ base.query_api_pattern 'DeleteBucketLifecycle', :delete, '{:Bucket}',
199
+ :params => { 'lifecycle' => nil }
200
+
201
+
202
+ #-----------------
203
+ # Object
204
+ #-----------------
205
+
206
+
207
+ base.query_api_pattern 'DeleteObject', :delete, '{:Bucket}/{:Object}'
208
+
209
+
210
+ base.query_api_pattern 'DeleteMultipleObjects', :post, '{:Bucket}',
211
+ :params => { 'delete' => nil },
212
+ :body => { 'Delete' => {
213
+ 'Quiet' => :Quiet,
214
+ 'Object[{:Object}]' => {
215
+ 'Key' => :Key,
216
+ 'VersionId' => :VersionId
217
+ }
218
+ }
219
+ },
220
+ :defaults => {
221
+ :VersionId => Utils::NONE,
222
+ :Quiet => Utils::NONE
223
+ },
224
+ :before => Proc.new{ |args| # fix incoming Keys if they were passed as Strings and not as Hashes
225
+ unless args[:params]['Object']._blank?
226
+ args[:params]['Object'] = Array(args[:params]['Object'])
227
+ args[:params]['Object'].dup.each_with_index do |object, idx|
228
+ args[:params]['Object'][idx] = { 'Key' => object } unless object.is_a?(Hash)
229
+ end
230
+ end
231
+ }
232
+
233
+
234
+ base.query_api_pattern 'GetObject', :get, '{:Bucket}/{:Object}',
235
+ :options => { :raw_response => true }
236
+
237
+
238
+ base.query_api_pattern 'GetObjectAcl', :get, '{:Bucket}/{:Object}',
239
+ :params => { 'acl' => nil }
240
+
241
+
242
+ base.query_api_pattern 'GetObjectTorrent', :get, '{:Bucket}/{:Object}',
243
+ :params => { 'torrent'=> nil }
244
+
245
+
246
+ base.query_api_pattern 'HeadObject', :head, '{:Bucket}/{:Object}'
247
+
248
+
249
+ base.query_api_pattern 'PostObject', :post, '{:Bucket}/{:Object}'
250
+
251
+
252
+ base.query_api_pattern 'PutObject', :put, '{:Bucket}/{:Object}',
253
+ :body => Utils::MUST_BE_SET,
254
+ :headers => { 'content-type' => 'application/octet-stream' }
255
+
256
+
257
+ base.query_api_pattern 'PutObjectAcl', :put, '{:Bucket}/{:Object}',
258
+ :params => { 'acl'=> nil },
259
+ :body => { 'AccessControlPolicy' => :AccessControlPolicy },
260
+ :headers => { 'content-type' => 'application/xml'}
261
+
262
+
263
+ base.query_api_pattern 'PutObjectCannedAcl', :put, '{:Bucket}/{:Object}',
264
+ :params => { 'acl' => nil },
265
+ :headers => { 'x-amz-acl' => :Acl, 'content-type' => 'application/xml'}
266
+
267
+
268
+ base.query_api_pattern 'CopyObject', :put, '{:DestinationBucket}/{:DestinationObject}',
269
+ :headers => { 'x-amz-copy-source' => '{:SourceBucket}/{:SourceObject}' }
270
+
271
+
272
+ base.query_api_pattern 'CopyObjectWithMetadata', :put, '{:DestinationBucket}/{:DestinationObject}',
273
+ :headers => { 'x-amz-copy-source' => '{:SourceBucket}/{:SourceObject}',
274
+ 'x-amz-metadata-directive' => :MetadataDirective },
275
+ :defaults => {
276
+ :MetadataDirective => 'COPY'
277
+ }
278
+
279
+
280
+ base.query_api_pattern 'InitiateMultipartUpload', :post, '{:Bucket}/{:Object}',
281
+ :params => { 'uploads' => nil }
282
+
283
+
284
+ base.query_api_pattern 'UploadPart', :post, '{:Bucket}/{:Object}',
285
+ :params => { 'partNumber' => :PartNumber,
286
+ 'uploadId' => :UploadId },
287
+ :headers => { 'content-type' => 'application/octet-stream' }
288
+
289
+
290
+ base.query_api_pattern 'UploadPartCopy', :put, '{:DestinationBucket}/{:DestinationObject}',
291
+ :params => { 'partNumber' => :PartNumber,
292
+ 'uploadId' => :UploadId },
293
+ :headers => { 'x-amz-copy-source' => '{:SourceBucket}/{:SourceObject}' }
294
+
295
+
296
+ base.query_api_pattern 'CompleteMultipartUpload', :post, '{:Bucket}/{:Object}',
297
+ :params => { 'uploadId' => :UploadId },
298
+ :body => { 'CompleteMultipartUpload' => :CompleteMultipartUpload },
299
+ :headers => { 'x-amz-acl' => :Acl, 'content-type' => 'application/xml'}
300
+
301
+
302
+ base.query_api_pattern 'AbortMultipartUpload', :delete, '{:Bucket}/{:Object}',
303
+ :params => { 'uploadId' => :UploadId }
304
+
305
+
306
+ base.query_api_pattern 'ListParts', :get, '{:Bucket}/{:Object}',
307
+ :params => { 'uploadId' => :UploadId }
308
+ end
309
+ end
310
+
311
+ end
312
+ end
313
+ end
314
+ end
315
+ end