right_aws_api 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/HISTORY +11 -9
- data/README.md +106 -0
- data/lib/cloud/aws/s3/manager.rb +32 -6
- data/lib/cloud/aws/s3/routines/request_signer.rb +13 -4
- data/lib/cloud/aws/s3/wrappers/default.rb +2 -2
- data/lib/right_aws_api_version.rb +1 -1
- data/spec/cloud/aws/s3/routines/request_signer_spec.rb +7 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64f50bf6419f2dab98f45aaf99cdba9cb1239e4f
|
4
|
+
data.tar.gz: 02dcede70b66b401f4a2fb1227881623da8a2979
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8624a2807021be070b88d46783d923df81961ddd802517e61eb74eb2358d28d995c03fe57df35a4ef711c76e39e67de8ed8c954ed66f14725315201ebc644c55
|
7
|
+
data.tar.gz: 5464bb232f2fa074ec5a26d8fd04df4c19fd3a7b71ff026ab14d273f791ba9a6ce0e333326d4c8fbf5b73cbd780252b85b140e7566314ccda47530c7454a3a3c
|
data/HISTORY
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
==
|
2
|
-
- v0.2.2
|
3
|
-
Added an ability to not use DNS like buckets in AWS S3
|
4
|
-
Some minor bugs were fixed in singature V4
|
1
|
+
== current
|
5
2
|
|
6
|
-
==
|
7
|
-
-
|
8
|
-
|
3
|
+
== 2015-01-16, v0.2.3
|
4
|
+
- Added an ability to not use DNS like buckets in AWS S3
|
5
|
+
- Added support for S3 folders creation
|
9
6
|
|
10
|
-
==
|
11
|
-
-
|
7
|
+
== 2015-01-16, v0.2.2
|
8
|
+
- Some minor bugs were fixed in singature V4
|
9
|
+
|
10
|
+
== 2014-10-30, v0.2.1
|
11
|
+
- Added support for AWS v4 signature and make it the default
|
12
|
+
|
13
|
+
== 2013-06-28, pre-release
|
data/README.md
CHANGED
@@ -66,6 +66,8 @@ call and you know what params it accepts - just call the method with those param
|
|
66
66
|
```ruby
|
67
67
|
require "right_aws_api"
|
68
68
|
|
69
|
+
ec2 = RightScale::CloudApi::AWS::EC2::Manager.new(key, secret, endpoint)
|
70
|
+
|
69
71
|
# Get a list of your instances
|
70
72
|
ec2.DescribeInstances
|
71
73
|
|
@@ -156,6 +158,110 @@ call and you know what params it accepts - just call the method with those param
|
|
156
158
|
sqs.DeleteQueue('myCoolQueue')
|
157
159
|
```
|
158
160
|
|
161
|
+
### Options
|
162
|
+
|
163
|
+
There is a way to provide extra options when you instantiate a new manager:
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
options = { :key => value }
|
167
|
+
ec2 = RightScale::CloudApi::AWS::EC2::Manager.new(key, secret, endpoint, options)
|
168
|
+
sqs = RightScale::CloudApi::AWS::SQS::Manager.new(key, secret, account_number, endpoint, options)
|
169
|
+
s3 = RightScale::CloudApi::AWS::S3::Manager::new(key, secret, endpoint, options)
|
170
|
+
etc
|
171
|
+
```
|
172
|
+
|
173
|
+
The options allow you to tweak the managers behavoir. Here is a list of the options that make sense for AWS services:
|
174
|
+
|
175
|
+
Name | Type | Default | Description
|
176
|
+
-------------------------| --------| ----------------------------|--------------
|
177
|
+
:abort_on_timeout | Boolean | false | When set to +true+ the gem does not perform a retry call when there is a connection timeout. This may help you to deal with request idempotence issue. Lets say you make a create call and you get back a timeout. It is possible that AWS created a new resource but just failed to report it properly. It is better to stop here rather than keep retrying creating more and more resources.
|
178
|
+
:api_version | String | see service Manager | The required cloud API version if it is different from the default one.
|
179
|
+
:cache | Boolean | false | Cache cloud responses when possible so that we don't parse them again if cloud response does not change (see cloud specific ApiManager definition).
|
180
|
+
:cloud | Hash | {} | A set of cloud or service specific options. There is an only option for AWS S3 so far: :no_dns_buckets => false/true.
|
181
|
+
:connection_open_timeout | Integer | up to NetHttpPersistent gem | Connection open timeout (in seconds).
|
182
|
+
:connection_read_timeout | Integer | up to NetHttpPersistent gem | Connection read timeout (in seconds).
|
183
|
+
:connection_retry_count | Integer | 3 | Max number of establish connection retry attempts before it gives up.
|
184
|
+
:connection_retry_delay | Float | 0.5 | Initial retry backoff delay in seconds. The value is doubled on every retry attempt.
|
185
|
+
:connection_verify_mode | Integer | OpenSSL::SSL::VERIFY_PEER | Try OpenSSL::SSL::VERIFY_NONE is there is an SSL sertificate issue with th eremote end. This may happen when working with DNS-line S3 buckets.
|
186
|
+
:headers | Hash | {} | A set of request headers to be added to every API request.
|
187
|
+
:logger | Logger | -> STDOUT | Current logger. When nil is given it logs to '/dev/nul'.
|
188
|
+
:log_filter_patterns | Array | see DEFAULT_LOG_FILTERS | A set of log filters that define what to log (see {RightScale::CloudApi::CloudApiLogger}).
|
189
|
+
:params | Hash | {} | A set of URL params to be added to every API request.
|
190
|
+
:raw_response | Boolean | false | By default the gem parses all XML and JSON responses and returns them as ruby Hashes. Sometimes it is not what one would want (Amazon S3 GetObject for example). Setting this option to +true+ forces the gem to return the body of the response as it is.
|
191
|
+
|
192
|
+
For more options see https://github.com/rightscale/right_cloud_api_base/blob/master/lib/base/api_manager.rb
|
193
|
+
|
194
|
+
### Response
|
195
|
+
|
196
|
+
It is easy to get the last HTTP request and response data:
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
s3.PutObject('Bucket' => 'a.b.c.d.1.com', 'Object' => '13', :body => 'hahaha') #=> ''
|
200
|
+
|
201
|
+
s3.request.verb # => 'put'
|
202
|
+
s3.request.path # => '/13'
|
203
|
+
s3.request.headers # =>
|
204
|
+
{
|
205
|
+
"content-type" => ["binary/octet-stream"],
|
206
|
+
"content-length" => [6],
|
207
|
+
"content-md5" => ["EBpuyfk4iF3wpE8gRY0utA=="],
|
208
|
+
"x-amz-content-sha256" => ["23453452345234523452345"],
|
209
|
+
"x-amz-date" => ["20150123T225348Z"],
|
210
|
+
"x-amz-expires" => [3600],
|
211
|
+
"host" => ["a.b.c.d.1.com.s3.amazonaws.com"],
|
212
|
+
"authorization" => ["AWS4-HMAC-SHA256 Credential=000/20150123/us-east-1/s3/aws4_request, SignedHeaders=content-length;content-md5;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-expires, Signature=111"]}
|
213
|
+
s3.request.body #=> "hahaha"
|
214
|
+
|
215
|
+
s3.response.code #=> '200'
|
216
|
+
s3.response.headers #=>
|
217
|
+
{
|
218
|
+
"x-amz-id-2" => ["wB/XOQ+dfgdfgdfgwgfdg"],
|
219
|
+
"x-amz-request-id" => ["FEC7DEE7C51ACAB3"],
|
220
|
+
"date" => ["Fri, 23 Jan 2015 22:53:49 GMT"],
|
221
|
+
"etag" => ["\"101a6ec9f938885df0a44f20458d2eb4\""],
|
222
|
+
"content-length" => ["0"],
|
223
|
+
"server" => ["AmazonS3"]}
|
224
|
+
s3.response.body #=> ''
|
225
|
+
|
226
|
+
```
|
227
|
+
|
228
|
+
Furthermore every response value may return its HTTP response code and HTTP headers through 'metadata' method:
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
response1 = s3.GetObject('Bucket' => 'a.b.c.d.1.com', 'Object' => '13')
|
232
|
+
response2 = s3.GetObject('Bucket' => 'a.b.c.d.1.com', 'Object' => '14')
|
233
|
+
|
234
|
+
response1 #=> "hahaha"
|
235
|
+
response1.metadata #=>
|
236
|
+
{ :code => '200',
|
237
|
+
:headers =>
|
238
|
+
{"x-amz-id-2" => ["sdasfdasd="],
|
239
|
+
"x-amz-request-id" => ["B2AB70B2D081BF2B"],
|
240
|
+
"date" => ["Fri, 23 Jan 2015 23:02:36 GMT"],
|
241
|
+
"last-modified" => ["Fri, 23 Jan 2015 22:53:49 GMT"],
|
242
|
+
"etag" => ["\"2345435234523452345324\""],
|
243
|
+
"accept-ranges" => ["bytes"],
|
244
|
+
"content-type" => ["binary/octet-stream"],
|
245
|
+
"content-length" => ["6"],
|
246
|
+
"server" => ["AmazonS3"]}}
|
247
|
+
|
248
|
+
response2 #=> "hohohohoho"
|
249
|
+
response2.metadata #=>
|
250
|
+
{ :code => '200',
|
251
|
+
:headers =>
|
252
|
+
{"x-amz-id-2" => ["asdasdf="],
|
253
|
+
"x-amz-request-id" => ["50DBC360C934C548"],
|
254
|
+
"date" => ["Fri, 23 Jan 2015 23:02:36 GMT"],
|
255
|
+
"last-modified" => ["Fri, 23 Jan 2015 23:01:55 GMT"],
|
256
|
+
"etag" => ["\"2345345243523452345234\""],
|
257
|
+
"accept-ranges" => ["bytes"],
|
258
|
+
"content-type" => ["binary/octet-stream"],
|
259
|
+
"content-length" => ["10"],
|
260
|
+
"server" => ["AmazonS3"]}}
|
261
|
+
```
|
262
|
+
|
263
|
+
|
264
|
+
|
159
265
|
## Dependencies
|
160
266
|
|
161
267
|
This gem depends on a base gem which is shared across all RightScale cloud libraries:
|
data/lib/cloud/aws/s3/manager.rb
CHANGED
@@ -38,13 +38,35 @@ module RightScale
|
|
38
38
|
|
39
39
|
# Amazon Simple Storage Service (S3) compatible manager (thread safe).
|
40
40
|
#
|
41
|
+
# There are 2 ways of using S3 API manager: _HTTP verb calls_ or _helper methods_
|
42
|
+
#
|
43
|
+
# ### HTTP verbs
|
44
|
+
#
|
45
|
+
# The manager supports following HTTP verbs: get, post, put, head and delete,
|
46
|
+
# and all of them share the same syntax:
|
47
|
+
#
|
48
|
+
# ```ruby
|
49
|
+
# s3.post(path, :params => Hash, :headers => Hash, :body => 'foo-bar', :options => Hash)
|
50
|
+
# ```
|
51
|
+
#
|
52
|
+
# ### Helper methods
|
53
|
+
#
|
54
|
+
# Eventhough the _HTTP verbs_ are powerfull they are not too handy.
|
55
|
+
# If you like to call API actions by their name you may find our helper methods usefull.
|
56
|
+
#
|
57
|
+
# In most cases these methods use the same names and they take the same parameters
|
58
|
+
# that Amazon uses in their docs.
|
59
|
+
#
|
60
|
+
# You can find use case examples for both the verbs and the methods below.
|
61
|
+
# The helper method definitions can be found here: https://github.com/rightscale/right_aws_api/blob/master/lib/cloud/aws/s3/wrappers/default.rb
|
62
|
+
#
|
41
63
|
# @example
|
42
64
|
# require "right_aws_api"
|
43
65
|
#
|
44
66
|
# s3 = RightScale::CloudApi::AWS::S3::Manager.new(key, secret, 'https://s3.amazonaws.com')
|
45
67
|
#
|
46
68
|
# @example
|
47
|
-
# # -- Using HTTP
|
69
|
+
# # -- Using HTTP verbs --
|
48
70
|
#
|
49
71
|
# # List all buckets
|
50
72
|
# s3.get
|
@@ -75,11 +97,13 @@ module RightScale
|
|
75
97
|
#
|
76
98
|
# # Put object
|
77
99
|
# # Do not forget to provide a proper 'content-type' header because the default
|
78
|
-
# # one is set to '
|
100
|
+
# # one is set to 'binary/octet-stream'
|
79
101
|
# s3.put('devs-us-east/boot1.jpg',
|
80
102
|
# :body => 'This is my object DATA. WooHoo!!!',
|
81
103
|
# :headers => {'content-type' => 'text/plain'})
|
82
104
|
#
|
105
|
+
# # Create a folder
|
106
|
+
# s3.put('devs-us-east/logs/')
|
83
107
|
#
|
84
108
|
# @example
|
85
109
|
# # A simple example of a multi-thread file download:
|
@@ -111,7 +135,6 @@ module RightScale
|
|
111
135
|
# end
|
112
136
|
# end
|
113
137
|
#
|
114
|
-
#
|
115
138
|
# @example
|
116
139
|
# # -- Using helper methods --
|
117
140
|
#
|
@@ -135,12 +158,15 @@ module RightScale
|
|
135
158
|
# :params => { 'response-content-type' => 'image/jpeg'})
|
136
159
|
#
|
137
160
|
# # Put object
|
138
|
-
# # P.S. 'content-type' is '
|
161
|
+
# # P.S. 'content-type' is 'binary/octet-stream' by default
|
139
162
|
# s3.PutObject('Bucket' => 'devs-us-east',
|
140
163
|
# 'Object' => 'boot1.jpg',
|
141
164
|
# :body => file_content,
|
142
165
|
# :headers => {'content-type' => 'image/jpeg'})
|
143
166
|
#
|
167
|
+
# # Create a folder
|
168
|
+
# s3.PutObject('Bucket' => 'devs-us-east', 'Object' => 'logs/', :body => '')
|
169
|
+
#
|
144
170
|
# @example
|
145
171
|
#
|
146
172
|
# # List all buckets
|
@@ -152,9 +178,9 @@ module RightScale
|
|
152
178
|
# "CreationDate"=>"2011-05-25T20:46:28.000Z"},
|
153
179
|
# {"Name"=>"CR_right_test",
|
154
180
|
# "CreationDate"=>"2011-06-08T20:46:32.000Z"},
|
155
|
-
# {"Name"=>"DarrylTest",
|
181
|
+
# {"Name"=>"DarrylTest",
|
156
182
|
# "CreationDate"=>"2011-06-03T03:43:08.000Z"},
|
157
|
-
# {"Name"=>"RightScalePeter",
|
183
|
+
# {"Name"=>"RightScalePeter",
|
158
184
|
# "CreationDate"=>"2008-10-28T03:59:20.000Z"}]},
|
159
185
|
# "Owner"=>
|
160
186
|
# {"DisplayName"=>"fghsfg",
|
@@ -120,8 +120,12 @@ module RightScale
|
|
120
120
|
# Amazon wants them to be escaped before we sign the request.
|
121
121
|
# P.S. but do not escape "/" (signature v4 does not like this)
|
122
122
|
#
|
123
|
-
|
124
|
-
|
123
|
+
new_bucket = $1
|
124
|
+
pre_object = $2.to_s
|
125
|
+
object = pre_object.split('/').map{|i| Utils::AWS::amz_escape(i)}.join('/')
|
126
|
+
# Preserve '/' if it is the last symbol of the object because it is an operation on a folder
|
127
|
+
object += '/' if object.length > 0 && pre_object[/\/$/]
|
128
|
+
[ new_bucket, object ]
|
125
129
|
end
|
126
130
|
|
127
131
|
|
@@ -183,7 +187,7 @@ module RightScale
|
|
183
187
|
# So we need to include it into our signature to avoid the error below:
|
184
188
|
# 'The request signature we calculated does not match the signature you provided.
|
185
189
|
# Check your key and signing method.'
|
186
|
-
headers.set_if_blank('content-type', '
|
190
|
+
headers.set_if_blank('content-type', 'binary/octet-stream')
|
187
191
|
headers
|
188
192
|
end
|
189
193
|
|
@@ -210,10 +214,15 @@ module RightScale
|
|
210
214
|
# @return [Boolean]
|
211
215
|
#
|
212
216
|
def is_dns_bucket?(bucket)
|
213
|
-
return false if
|
217
|
+
return false if no_dns_buckets?
|
214
218
|
Utils::AWS::is_dns_bucket?(bucket)
|
215
219
|
end
|
216
220
|
|
221
|
+
|
222
|
+
def no_dns_buckets?
|
223
|
+
!!@data[:options][:cloud][:no_dns_buckets]
|
224
|
+
end
|
225
|
+
|
217
226
|
end
|
218
227
|
end
|
219
228
|
end
|
@@ -253,7 +253,7 @@ module RightScale
|
|
253
253
|
|
254
254
|
base.query_api_pattern 'PutObject', :put, '{:Bucket}/{:Object}',
|
255
255
|
:body => Utils::MUST_BE_SET,
|
256
|
-
:headers => { 'content-type' => '
|
256
|
+
:headers => { 'content-type' => 'binary/octet-stream' }
|
257
257
|
|
258
258
|
|
259
259
|
base.query_api_pattern 'PutObjectAcl', :put, '{:Bucket}/{:Object}',
|
@@ -286,7 +286,7 @@ module RightScale
|
|
286
286
|
base.query_api_pattern 'UploadPart', :post, '{:Bucket}/{:Object}',
|
287
287
|
:params => { 'partNumber' => :PartNumber,
|
288
288
|
'uploadId' => :UploadId },
|
289
|
-
:headers => { 'content-type' => '
|
289
|
+
:headers => { 'content-type' => 'binary/octet-stream' }
|
290
290
|
|
291
291
|
|
292
292
|
base.query_api_pattern 'UploadPartCopy', :put, '{:DestinationBucket}/{:DestinationObject}',
|
@@ -37,6 +37,10 @@ describe RightScale::CloudApi::AWS::S3::RequestSigner do
|
|
37
37
|
|
38
38
|
|
39
39
|
context '#compute_host' do
|
40
|
+
before :each do
|
41
|
+
allow(subject).to receive(:no_dns_buckets?).and_return(false)
|
42
|
+
end
|
43
|
+
|
40
44
|
context 'DNS bucket' do
|
41
45
|
before :each do
|
42
46
|
bucket = 'foo-bar-bucket'
|
@@ -88,9 +92,9 @@ describe RightScale::CloudApi::AWS::S3::RequestSigner do
|
|
88
92
|
end
|
89
93
|
|
90
94
|
context 'content-type' do
|
91
|
-
it 'defaults content-type to
|
95
|
+
it 'defaults content-type to binary/octet-stream' do
|
92
96
|
result = subject.compute_headers!(@headers, @body, @host)
|
93
|
-
expectation = ['
|
97
|
+
expectation = ['binary/octet-stream']
|
94
98
|
expect(result['content-type']).to eq(expectation)
|
95
99
|
end
|
96
100
|
end
|
@@ -101,6 +105,7 @@ describe RightScale::CloudApi::AWS::S3::RequestSigner do
|
|
101
105
|
before :each do
|
102
106
|
@path = 'foo-bar'
|
103
107
|
@object = 'foo%2Fbar%2F%D0%B1%D0%B0%D0%BD%D0%B0%D0%BD%D0%B0.jpg'
|
108
|
+
allow(subject).to receive(:no_dns_buckets?).and_return(false)
|
104
109
|
end
|
105
110
|
|
106
111
|
it 'works for DNS bucket' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: right_aws_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- RightScale, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-02-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: right_cloud_api_base
|