right_aws_api 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -61,11 +61,11 @@ module RightScale
61
61
 
62
62
 
63
63
  base.query_api_pattern 'DeleteBucketPolicy', :delete, '{:Bucket}',
64
- :params => { 'policy' => nil }
64
+ :params => { 'policy' => '' }
65
65
 
66
66
 
67
67
  base.query_api_pattern 'DeleteBucketWebsite', :delete, '{:Bucket}',
68
- :params => { 'website' => nil }
68
+ :params => { 'website' => '' }
69
69
 
70
70
 
71
71
  base.query_api_pattern 'ListObjects', :get, '{:Bucket}'
@@ -73,130 +73,131 @@ module RightScale
73
73
 
74
74
 
75
75
  base.query_api_pattern 'GetBucketAcl', :get, '{:Bucket}',
76
- :params => { 'acl'=> nil }
76
+ :params => { 'acl'=> '' }
77
77
 
78
78
 
79
79
  base.query_api_pattern 'GetBucketPolicy', :get, '{:Bucket}',
80
- :params => { 'policy'=> nil }
80
+ :params => { 'policy'=> '' }
81
81
 
82
82
 
83
83
  base.query_api_pattern 'GetBucketLocation', :get, '{:Bucket}',
84
- :params => { 'location'=> nil }
84
+ :params => { 'location'=> '' }
85
85
 
86
86
 
87
87
  base.query_api_pattern 'GetBucketLogging', :get, '{:Bucket}',
88
- :params => { 'logging'=> nil }
88
+ :params => { 'logging'=> '' }
89
89
 
90
90
 
91
91
  base.query_api_pattern 'GetBucketNotification', :get, '{:Bucket}',
92
- :params => { 'notification'=> nil }
92
+ :params => { 'notification'=> '' }
93
93
 
94
94
 
95
95
  base.query_api_pattern 'GetBucketVersions', :get, '{:Bucket}',
96
- :params => { 'versions'=> nil }
96
+ :params => { 'versions'=> '' }
97
97
 
98
98
 
99
99
  base.query_api_pattern 'GetBucketRequestPayment', :get, '{:Bucket}',
100
- :params => { 'requestPayment'=> nil }
100
+ :params => { 'requestPayment'=> '' }
101
101
 
102
102
 
103
103
  base.query_api_pattern 'GetBucketVersioning', :get, '{:Bucket}',
104
- :params => { 'versioning'=> nil }
104
+ :params => { 'versioning'=> '' }
105
105
 
106
106
 
107
107
  base.query_api_pattern 'GetBucketWebsite', :get, '{:Bucket}',
108
- :params => { 'website'=> nil }
108
+ :params => { 'website'=> '' }
109
109
 
110
110
 
111
111
  base.query_api_pattern 'ListMultipartUploads', :get, '{:Bucket}',
112
- :params => { 'uploads'=> nil }
112
+ :params => { 'uploads'=> '' }
113
113
 
114
114
 
115
- base.query_api_pattern 'PutBucket', :put, '{:Bucket}'
115
+ base.query_api_pattern 'PutBucket', :put, '{:Bucket}',
116
+ :headers => { 'content-type' => 'application/xml' }
116
117
 
117
118
 
118
119
  base.query_api_pattern 'PutBucketAcl', :put, '{:Bucket}',
119
- :params => { 'acl' => nil },
120
+ :params => { 'acl' => '' },
120
121
  :body => { 'AccessControlPolicy' => :AccessControlPolicy },
121
122
  :headers => { 'content-type' => 'application/xml'}
122
123
 
123
124
 
124
125
  base.query_api_pattern 'PutBucketPolicy', :put, '{:Bucket}',
125
- :params => { 'policy' => nil },
126
+ :params => { 'policy' => '' },
126
127
  :body => Utils::MUST_BE_SET,
127
128
  :headers => { 'content-type' => 'application/json' }
128
129
 
129
130
 
130
131
  base.query_api_pattern 'PutBucketLogging', :put, '{:Bucket}',
131
- :params => { 'logging' => nil },
132
+ :params => { 'logging' => '' },
132
133
  :body => { 'BucketLoggingStatus' => :BucketLoggingStatus },
133
134
  :headers => { 'content-type' => 'application/xml'}
134
135
 
135
136
 
136
137
  base.query_api_pattern 'PutBucketNotification', :put, '{:Bucket}',
137
- :params => { 'notification' => nil },
138
+ :params => { 'notification' => '' },
138
139
  :body => { 'NotificationConfiguration' => :NotificationConfiguration },
139
140
  :headers => { 'content-type' => 'application/xml'}
140
141
 
141
142
 
142
143
  base.query_api_pattern 'PutBucketRequestPayment', :put, '{:Bucket}',
143
- :params => { 'requestPayment' => nil },
144
+ :params => { 'requestPayment' => '' },
144
145
  :body => { 'RequestPaymentConfiguration' => :RequestPaymentConfiguration },
145
146
  :headers => { 'content-type' => 'application/xml'}
146
147
 
147
148
 
148
149
  base.query_api_pattern 'PutBucketVersioning', :put, '{:Bucket}',
149
- :params => { 'versioning' => nil },
150
+ :params => { 'versioning' => '' },
150
151
  :body => { 'VersioningConfiguration' => :VersioningConfiguration },
151
152
  :headers => { 'content-type' => 'application/xml'}
152
153
 
153
154
 
154
155
  base.query_api_pattern 'PutBucketWebsite', :put, '{:Bucket}',
155
- :params => { 'website' => nil },
156
+ :params => { 'website' => '' },
156
157
  :body => { 'WebsiteConfiguration' => :WebsiteConfiguration },
157
158
  :headers => { 'content-type' => 'application/xml'}
158
159
 
159
160
 
160
161
  base.query_api_pattern 'GetBucketCors', :get, '{:Bucket}',
161
- :params => { 'cors'=> nil }
162
+ :params => { 'cors'=> '' }
162
163
 
163
164
 
164
165
  base.query_api_pattern 'DeleteBucketCors', :delete, '{:Bucket}',
165
- :params => { 'cors'=> nil }
166
+ :params => { 'cors'=> '' }
166
167
 
167
168
 
168
169
  base.query_api_pattern 'PutBucketCors', :put, '{:Bucket}',
169
- :params => { 'cors'=> nil },
170
+ :params => { 'cors'=> '' },
170
171
  :body => { 'CORSConfiguration' => { 'CORSRule' => :CORSRule } },
171
172
  :headers => { 'content-type' => 'application/xml'}
172
173
 
173
174
 
174
175
  base.query_api_pattern 'GetBucketTagging', :get, '{:Bucket}',
175
- :params => { 'tagging' => nil }
176
+ :params => { 'tagging' => '' }
176
177
 
177
178
 
178
179
  base.query_api_pattern 'PutBucketTagging', :put, '{:Bucket}',
179
- :params => { 'tagging' => nil },
180
+ :params => { 'tagging' => '' },
180
181
  :body => { 'Tagging' => { 'TagSet' => { 'Tag' => :TagSet } } },
181
182
  :headers => { 'content-type' => 'application/xml'}
182
183
 
183
184
 
184
185
  base.query_api_pattern 'DeleteBucketTagging', :delete, '{:Bucket}',
185
- :params => { 'tagging' => nil }
186
+ :params => { 'tagging' => '' }
186
187
 
187
188
 
188
189
  base.query_api_pattern 'GetBucketLifecycle', :get, '{:Bucket}',
189
- :params => { 'lifecycle' => nil }
190
+ :params => { 'lifecycle' => '' }
190
191
 
191
192
 
192
193
  base.query_api_pattern 'PutBucketLifecycle', :put, '{:Bucket}',
193
- :params => { 'lifecycle' => nil },
194
+ :params => { 'lifecycle' => '' },
194
195
  :body => { 'LifecycleConfiguration' => { 'Rule' => :Rule } },
195
196
  :headers => { 'content-type' => 'application/xml'}
196
197
 
197
198
 
198
199
  base.query_api_pattern 'DeleteBucketLifecycle', :delete, '{:Bucket}',
199
- :params => { 'lifecycle' => nil }
200
+ :params => { 'lifecycle' => '' }
200
201
 
201
202
 
202
203
  #-----------------
@@ -208,7 +209,8 @@ module RightScale
208
209
 
209
210
 
210
211
  base.query_api_pattern 'DeleteMultipleObjects', :post, '{:Bucket}',
211
- :params => { 'delete' => nil },
212
+ :headers => { 'content-type' => 'application/xml'},
213
+ :params => { 'delete' => '' },
212
214
  :body => { 'Delete' => {
213
215
  'Quiet' => :Quiet,
214
216
  'Object[{:Object}]' => {
@@ -236,11 +238,11 @@ module RightScale
236
238
 
237
239
 
238
240
  base.query_api_pattern 'GetObjectAcl', :get, '{:Bucket}/{:Object}',
239
- :params => { 'acl' => nil }
241
+ :params => { 'acl' => '' }
240
242
 
241
243
 
242
244
  base.query_api_pattern 'GetObjectTorrent', :get, '{:Bucket}/{:Object}',
243
- :params => { 'torrent'=> nil }
245
+ :params => { 'torrent'=> '' }
244
246
 
245
247
 
246
248
  base.query_api_pattern 'HeadObject', :head, '{:Bucket}/{:Object}'
@@ -255,13 +257,13 @@ module RightScale
255
257
 
256
258
 
257
259
  base.query_api_pattern 'PutObjectAcl', :put, '{:Bucket}/{:Object}',
258
- :params => { 'acl'=> nil },
260
+ :params => { 'acl'=> '' },
259
261
  :body => { 'AccessControlPolicy' => :AccessControlPolicy },
260
262
  :headers => { 'content-type' => 'application/xml'}
261
263
 
262
264
 
263
265
  base.query_api_pattern 'PutObjectCannedAcl', :put, '{:Bucket}/{:Object}',
264
- :params => { 'acl' => nil },
266
+ :params => { 'acl' => '' },
265
267
  :headers => { 'x-amz-acl' => :Acl, 'content-type' => 'application/xml'}
266
268
 
267
269
 
@@ -278,7 +280,7 @@ module RightScale
278
280
 
279
281
 
280
282
  base.query_api_pattern 'InitiateMultipartUpload', :post, '{:Bucket}/{:Object}',
281
- :params => { 'uploads' => nil }
283
+ :params => { 'uploads' => '' }
282
284
 
283
285
 
284
286
  base.query_api_pattern 'UploadPart', :post, '{:Bucket}/{:Object}',
@@ -22,7 +22,7 @@
22
22
  #++
23
23
 
24
24
  require "cloud/aws/base/helpers/utils"
25
- require "cloud/aws/base/routines/request_signer"
25
+ require "cloud/aws/sqs/routines/request_signer"
26
26
  require "cloud/aws/base/parsers/response_error"
27
27
 
28
28
  module RightScale
@@ -206,7 +206,7 @@ module RightScale
206
206
 
207
207
  set_routine CloudApi::RetryManager
208
208
  set_routine CloudApi::RequestInitializer
209
- set_routine AWS::RequestSigner
209
+ set_routine AWS::SQS::RequestSigner
210
210
  set_routine CloudApi::RequestGenerator
211
211
  set_routine CloudApi::RequestAnalyzer
212
212
  set_routine CloudApi::ConnectionProxy
@@ -0,0 +1,62 @@
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 SQS
28
+ # SQS Request signer
29
+ class RequestSigner < CloudApi::Routine
30
+
31
+ # RequestSigner error
32
+ class Error < CloudApi::Error
33
+ end
34
+
35
+ # Authenticates an SQS request
36
+ #
37
+ # @return [void]
38
+ #
39
+ # @example
40
+ # no example
41
+ #
42
+ def process
43
+ # Compile a final request path
44
+ @data[:request][:path] = Utils::join_urn(@data[:connection][:uri].path, @data[:request][:relative_path])
45
+
46
+ # Swap query params and body
47
+ @data[:request][:params]['Version'] ||= @data[:options][:api_version]
48
+ #@data[:request][:body] = Utils::params_to_urn(@data[:request][:params]){ |value| Utils::AWS::amz_escape(value) }
49
+ #@data[:request][:params] = {}
50
+
51
+ Utils::AWS::sign_v4_signature(
52
+ @data[:credentials][:aws_access_key_id],
53
+ @data[:credentials][:aws_secret_access_key],
54
+ @data[:connection][:uri].host,
55
+ @data[:request]
56
+ )
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -33,7 +33,7 @@ module RightScale
33
33
  # Gem version namespace
34
34
  module VERSION
35
35
  # Current version
36
- STRING = '0.2.0'
36
+ STRING = '0.2.2'
37
37
  end
38
38
  end
39
39
  end
@@ -35,10 +35,11 @@ Gem::Specification.new do |spec|
35
35
  spec.require_path = 'lib'
36
36
  spec.required_ruby_version = '>= 1.8.7'
37
37
 
38
- spec.add_dependency 'right_cloud_api_base', '>= 0.1.0'
38
+ spec.add_dependency 'right_cloud_api_base', '>= 0.2.1'
39
39
 
40
40
  spec.add_development_dependency 'rake'
41
41
  spec.add_development_dependency 'rspec', '>= 3.0.0'
42
+ spec.add_development_dependency 'timecop'
42
43
 
43
44
  spec.description = <<-EOF
44
45
  == DESCRIPTION:
@@ -0,0 +1,298 @@
1
+ require_relative '../../../../../lib/right_aws_api'
2
+
3
+ require 'rspec'
4
+ require 'timecop'
5
+
6
+ describe RightScale::CloudApi::Utils::AWS do
7
+ subject do
8
+ RightScale::CloudApi::Utils::AWS
9
+ end
10
+
11
+ context "signature v4" do
12
+
13
+ context "sign_v4_get_service_and_region" do
14
+ context "weird unknown host" do
15
+ it "fails" do
16
+ expect{
17
+ subject.sign_v4_get_service_and_region('foo.bar.com')
18
+ }.to raise_error(ArgumentError)
19
+ end
20
+ end
21
+
22
+ context "host with no service" do
23
+ it "fails" do
24
+ expect{
25
+ subject.sign_v4_get_service_and_region('foo.amazonaws.com')
26
+ }.to raise_error(ArgumentError,/service/)
27
+ end
28
+ end
29
+
30
+ context "S3" do
31
+ it "works" do
32
+ expect(
33
+ subject.sign_v4_get_service_and_region('s3.amazonaws.com')
34
+ ).to eq ['s3', 'us-east-1']
35
+
36
+ expect(
37
+ subject.sign_v4_get_service_and_region('xxx.s3.amazonaws.com')
38
+ ).to eq ['s3', 'us-east-1']
39
+
40
+ expect(
41
+ subject.sign_v4_get_service_and_region('s3-external-1.amazonaws.com')
42
+ ).to eq ['s3', 'us-east-1']
43
+
44
+ expect(
45
+ subject.sign_v4_get_service_and_region('s3-region-1.amazonaws.com')
46
+ ).to eq ['s3', 'region-1']
47
+
48
+ expect(
49
+ subject.sign_v4_get_service_and_region('xxx.s3-region-1.amazonaws.com')
50
+ ).to eq ['s3', 'region-1']
51
+
52
+ expect(
53
+ subject.sign_v4_get_service_and_region('s3.region-1.amazonaws.com')
54
+ ).to eq ['s3', 'region-1']
55
+
56
+ expect(
57
+ subject.sign_v4_get_service_and_region('xxx.s3.region-1.amazonaws.com')
58
+ ).to eq ['s3', 'region-1']
59
+ end
60
+ end
61
+
62
+
63
+ context "any other service with standard endpoint" do
64
+ it "works" do
65
+ expect(
66
+ subject.sign_v4_get_service_and_region('ec2.region-1.amazonaws.com')
67
+ ).to eq ['ec2', 'region-1']
68
+ end
69
+ end
70
+ end
71
+
72
+
73
+ context "sign_v4_get_canonical_verb" do
74
+ it "uppercases the given verb" do
75
+ expect( subject.sign_v4_get_canonical_verb(:delete) ).to eq 'DELETE'
76
+ end
77
+ end
78
+
79
+
80
+ context "sign_v4_get_canonical_path" do
81
+ it "does nothing to the given path" do
82
+ expect( subject.sign_v4_get_canonical_path('foo/bar') ).to eq 'foo/bar'
83
+ end
84
+ end
85
+
86
+
87
+ context "sign_v4_get_canonical_headers" do
88
+ it "works" do
89
+ headers = {
90
+ 'foo' => 'x',
91
+ 'bar' => 'y,z'
92
+ }
93
+ expect(
94
+ subject.sign_v4_get_canonical_headers(headers)
95
+ ).to eq "bar:y,z\nfoo:x"
96
+ end
97
+ end
98
+
99
+
100
+ context "sign_v4_get_signed_headers" do
101
+ it "works" do
102
+ headers = {
103
+ 'foo' => 'x',
104
+ 'bar' => 'y,z'
105
+ }
106
+ expect(
107
+ subject.sign_v4_get_signed_headers(headers)
108
+ ).to eq "bar;foo"
109
+ end
110
+ end
111
+
112
+
113
+ context "sign_v4_headers" do
114
+ it "builds credential params" do
115
+ request = {
116
+ :headers => RightScale::CloudApi::HTTPHeaders.new,
117
+ :params => { 'A' => 'B' },
118
+ :body => "banana"
119
+ }
120
+ expect(
121
+ subject.sign_v4_headers(
122
+ request,
123
+ "ec2.region-1.amazonaws.com",
124
+ "20141103T150750Z"
125
+ )
126
+ ).to eq "b493d48364afe44d11c0165cf470a4164d1e2609911ef998be868d46ade3de4e"
127
+
128
+ expect(request).to eq(
129
+ {
130
+ :body => "banana",
131
+ :params => { 'A' => 'B' },
132
+ :headers => {
133
+ "content-length" => [6],
134
+ "content-type" => ["application/x-www-form-urlencoded; charset=utf-8"],
135
+ "content-md5" => ["crMCvyl6Iop1cwEj7+98QQ=="],
136
+ "x-amz-content-sha256" => ["b493d48364afe44d11c0165cf470a4164d1e2609911ef998be868d46ade3de4e"],
137
+ "x-amz-date" => ["20141103T150750Z"],
138
+ "x-amz-expires" => [3600]
139
+ }
140
+ }
141
+ )
142
+ end
143
+ end
144
+
145
+
146
+ context "sign_v4_query_params" do
147
+ it "builds credential params" do
148
+ request = { :params => {} }
149
+ expect(
150
+ subject.sign_v4_query_params(
151
+ request,
152
+ "AWS4-HMAC-SHA256",
153
+ "20141103T150750Z",
154
+ "host;content-type",
155
+ "aws_access_key",
156
+ "20141103/region-1/ec2/aws4_request"
157
+ )
158
+ ).to eq "UNSIGNED-PAYLOAD"
159
+
160
+ expect(request).to eq(
161
+ {
162
+ :params => {
163
+ "X-Amz-Date" => "20141103T150750Z",
164
+ "X-Amz-Expires" => 3600,
165
+ "X-Amz-Algorithm" => "AWS4-HMAC-SHA256",
166
+ "X-Amz-SignedHeaders" => "host;content-type",
167
+ "X-Amz-Credential" => "aws_access_key/20141103/region-1/ec2/aws4_request"
168
+ }
169
+ }
170
+ )
171
+ end
172
+ end
173
+
174
+
175
+ context "sign_v4_get_canonical_string" do
176
+ it "joins things properly" do
177
+ expect(
178
+ subject.sign_v4_get_canonical_string(
179
+ 'verb',
180
+ 'path',
181
+ 'query_string',
182
+ 'headers',
183
+ 'signed_headers',
184
+ 'payload'
185
+ )
186
+ ).to eq "verb\npath\nquery_string\nheaders\n\nsigned_headers\npayload"
187
+ end
188
+ end
189
+
190
+
191
+ context "sign_v4_get_string_to_sign" do
192
+ it "joins things properly" do
193
+ expect(
194
+ subject.sign_v4_get_string_to_sign('algorithm', 'current_time', 'creds_scope', 'canonical_string')
195
+ ).to eq "algorithm\ncurrent_time\ncreds_scope\ne32453154e605024921ee9c5eb4ee7eba458cbd2b387c9ac74e729f9fd7cb81b"
196
+ end
197
+ end
198
+
199
+
200
+ context "sign_v4_get_signature_key" do
201
+ it "works" do
202
+ expect(
203
+ subject.sign_v4_get_signature_key('key', 'string_to_sign', 'date', 'region', 'service')
204
+ ).to eq "0eb6bf1678c804d70bdda801326b3b70080ff19f2cb381e28231b2dcdd445b7c"
205
+ end
206
+ end
207
+
208
+
209
+ context "hex_encode" do
210
+ it "works" do
211
+ expect(
212
+ subject.hex_encode('HelloWorld!')
213
+ ).to eq "48656c6c6f576f726c6421"
214
+ end
215
+ end
216
+
217
+
218
+ context "sign_v4_signature" do
219
+ context "signature in headers" do
220
+ it 'works' do
221
+ request = {
222
+ :verb => :post,
223
+ :path => '/foo/bar',
224
+ :headers => RightScale::CloudApi::HTTPHeaders.new,
225
+ :params => { 'A' => 'B' },
226
+ :body => "banana"
227
+ }
228
+ Timecop.freeze(Time.local(2014)) do
229
+ subject.sign_v4_signature(
230
+ 'aws_access_key',
231
+ 'aws_secret_access_key',
232
+ 'ec2.us-region-1.amazonaws.com',
233
+ request,
234
+ )
235
+ end
236
+ expect(request).to eq(
237
+ {
238
+ :body => "banana",
239
+ :headers => {
240
+ "content-length" => [6],
241
+ "content-type" => ["application/x-www-form-urlencoded; charset=utf-8"],
242
+ "content-md5" => ["crMCvyl6Iop1cwEj7+98QQ=="],
243
+ "x-amz-content-sha256" => ["b493d48364afe44d11c0165cf470a4164d1e2609911ef998be868d46ade3de4e"],
244
+ "x-amz-date" => ["20140101T080000Z"],
245
+ "x-amz-expires" => [3600],
246
+ "host" => ["ec2.us-region-1.amazonaws.com"],
247
+ "authorization" => ["AWS4-HMAC-SHA256 Credential=aws_access_key/20140101/us-region-1/ec2/aws4_request, SignedHeaders=content-length;content-md5;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-expires, Signature=c7814d3865b530322742b73670475ac5adb0c1bd4ab39ecf9b80d672283fc177"]
248
+ },
249
+ :params => {"A" => "B"},
250
+ :path => "/foo/bar?A=B",
251
+ :verb => :post
252
+ }
253
+ )
254
+ end
255
+ end
256
+
257
+
258
+ context "signature in query params" do
259
+ it 'works' do
260
+ request = {
261
+ :verb => :post,
262
+ :path => '/foo/bar',
263
+ :headers => RightScale::CloudApi::HTTPHeaders.new,
264
+ :params => { 'A' => 'B' },
265
+ :body => "banana"
266
+ }
267
+ Timecop.freeze(Time.local(2014)) do
268
+ subject.sign_v4_signature(
269
+ 'aws_access_key',
270
+ 'aws_secret_access_key',
271
+ 'ec2.us-region-1.amazonaws.com',
272
+ request,
273
+ :query_params
274
+ )
275
+ end
276
+ expect(request).to eq(
277
+ {
278
+ :body => "banana",
279
+ :headers => {"host"=>["ec2.us-region-1.amazonaws.com"]},
280
+ :params => {
281
+ "A" => "B",
282
+ "X-Amz-Date" => "20140101T080000Z",
283
+ "X-Amz-Expires" => 3600,
284
+ "X-Amz-Algorithm" => "AWS4-HMAC-SHA256",
285
+ "X-Amz-SignedHeaders" => "host",
286
+ "X-Amz-Credential" => "aws_access_key/20140101/us-region-1/ec2/aws4_request"
287
+ },
288
+ :path => "/foo/bar?A=B&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=aws_access_key%2F20140101%2Fus-region-1%2Fec2%2Faws4_request&X-Amz-Date=20140101T080000Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=0fcac655717af1f947fc0942cc2a2e98fddfe71cf669b0a344235bb83684c9cf",
289
+ :verb => :post
290
+ }
291
+ )
292
+ end
293
+ end
294
+
295
+ end
296
+
297
+ end
298
+ end