aws 2.3.34 → 2.4.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.
- data/lib/acf/{right_acf_interface.rb → acf_interface.rb} +87 -57
- data/lib/aws.rb +9 -9
- data/lib/awsbase/aws_response_array.rb +21 -21
- data/lib/awsbase/awsbase.rb +579 -0
- data/lib/awsbase/benchmark_fix.rb +2 -2
- data/lib/awsbase/errors.rb +288 -0
- data/lib/awsbase/parsers.rb +227 -0
- data/lib/awsbase/support.rb +114 -114
- data/lib/awsbase/utils.rb +207 -0
- data/lib/ec2/ec2.rb +2070 -0
- data/lib/ec2/mon_interface.rb +243 -0
- data/lib/elb/elb_interface.rb +278 -276
- data/lib/iam/iam.rb +111 -109
- data/lib/rds/rds.rb +3 -1
- data/lib/right_aws.rb +9 -9
- data/lib/s3/bucket.rb +265 -0
- data/lib/s3/grantee.rb +238 -0
- data/lib/s3/key.rb +281 -0
- data/lib/s3/s3.rb +346 -0
- data/lib/s3/s3_interface.rb +1275 -0
- data/lib/sdb/active_sdb.rb +2 -0
- data/lib/sdb/sdb_interface.rb +886 -0
- data/lib/sqs/{right_sqs.rb → sqs.rb} +10 -0
- data/lib/sqs/sqs_interface.rb +483 -0
- data/test/s3/s3_test_base.rb +23 -0
- data/test/s3/test_s3.rb +156 -419
- data/test/s3/test_s3_class.rb +179 -0
- data/test/s3/test_s3_rights.rb +139 -0
- data/test/sqs/test_sqs.rb +19 -0
- metadata +27 -15
- data/lib/awsbase/right_awsbase.rb +0 -1254
- data/lib/ec2/right_ec2.rb +0 -1884
- data/lib/ec2/right_mon_interface.rb +0 -232
- data/lib/s3/right_s3.rb +0 -1115
- data/lib/s3/right_s3_interface.rb +0 -1253
- data/lib/sdb/right_sdb_interface.rb +0 -881
- data/lib/sqs/right_sqs_interface.rb +0 -443
@@ -73,7 +73,7 @@ module Aws
|
|
73
73
|
# acf.set_distribution_config(distibution[:aws_id], config) #=> true
|
74
74
|
#
|
75
75
|
class AcfInterface < AwsBase
|
76
|
-
|
76
|
+
|
77
77
|
include AwsBaseInterface
|
78
78
|
|
79
79
|
API_VERSION = "2010-08-01"
|
@@ -82,10 +82,19 @@ module Aws
|
|
82
82
|
DEFAULT_PROTOCOL = 'https'
|
83
83
|
DEFAULT_PATH = '/'
|
84
84
|
|
85
|
-
|
85
|
+
|
86
|
+
def self.connection_name
|
87
|
+
:acf_connection
|
88
|
+
end
|
89
|
+
|
90
|
+
@@bench = AwsBenchmarkingBlock.new
|
91
|
+
def self.bench
|
92
|
+
@@bench
|
93
|
+
end
|
86
94
|
def self.bench_xml
|
87
95
|
@@bench.xml
|
88
96
|
end
|
97
|
+
|
89
98
|
def self.bench_service
|
90
99
|
@@bench.service
|
91
100
|
end
|
@@ -104,13 +113,13 @@ module Aws
|
|
104
113
|
# {:multi_thread => true, :logger => Logger.new('/tmp/x.log')}) #=> #<Aws::AcfInterface::0xb7b3c30c>
|
105
114
|
#
|
106
115
|
def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
|
107
|
-
init({
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
aws_access_key_id
|
113
|
-
aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'],
|
116
|
+
init({:name => 'ACF',
|
117
|
+
:default_host => ENV['ACF_URL'] ? URI.parse(ENV['ACF_URL']).host : DEFAULT_HOST,
|
118
|
+
:default_port => ENV['ACF_URL'] ? URI.parse(ENV['ACF_URL']).port : DEFAULT_PORT,
|
119
|
+
:default_service => ENV['ACF_URL'] ? URI.parse(ENV['ACF_URL']).path : DEFAULT_PATH,
|
120
|
+
:default_protocol => ENV['ACF_URL'] ? URI.parse(ENV['ACF_URL']).scheme : DEFAULT_PROTOCOL},
|
121
|
+
aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'],
|
122
|
+
aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'],
|
114
123
|
params)
|
115
124
|
end
|
116
125
|
|
@@ -119,29 +128,30 @@ module Aws
|
|
119
128
|
#-----------------------------------------------------------------
|
120
129
|
|
121
130
|
# Generates request hash for REST API.
|
122
|
-
def generate_request(method, path, body=nil, headers={})
|
131
|
+
def generate_request(method, path, body=nil, headers={}) # :nodoc:
|
123
132
|
headers['content-type'] ||= 'text/xml' if body
|
124
|
-
headers['date']
|
133
|
+
headers['date'] = Time.now.httpdate
|
125
134
|
# Auth
|
126
|
-
signature
|
135
|
+
signature = AwsUtils::sign(@aws_secret_access_key, headers['date'])
|
127
136
|
headers['Authorization'] = "AWS #{@aws_access_key_id}:#{signature}"
|
128
137
|
# Request
|
129
|
-
path
|
130
|
-
request
|
138
|
+
path = "#{@params[:default_service]}/#{API_VERSION}/#{path}"
|
139
|
+
request = "Net::HTTP::#{method.capitalize}".constantize.new(path)
|
131
140
|
request.body = body if body
|
132
141
|
# Set request headers
|
133
142
|
headers.each { |key, value| request[key.to_s] = value }
|
134
143
|
# prepare output hash
|
135
|
-
{
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
144
|
+
{:request => request,
|
145
|
+
:server => @params[:server],
|
146
|
+
:port => @params[:port],
|
147
|
+
:protocol => @params[:protocol]}
|
148
|
+
end
|
149
|
+
|
150
|
+
# Sends request to Amazon and parses the response.
|
151
|
+
# Raises AwsError if any banana happened.
|
152
|
+
# todo: remove this and switch to using request_info2
|
143
153
|
def request_info(request, parser, options={}, &block) # :nodoc:
|
144
|
-
conn = get_conn(
|
154
|
+
conn = get_conn(self.class.connection_name, @params, @logger)
|
145
155
|
request_info_impl(conn, @@bench, request, parser, options, &block)
|
146
156
|
end
|
147
157
|
|
@@ -163,13 +173,13 @@ module Aws
|
|
163
173
|
|
164
174
|
def generate_call_reference # :nodoc:
|
165
175
|
result = Time.now.strftime('%Y%m%d%H%M%S')
|
166
|
-
10.times{ result << rand(10).to_s }
|
176
|
+
10.times { result << rand(10).to_s }
|
167
177
|
result
|
168
178
|
end
|
169
179
|
|
170
180
|
def merge_headers(hash) # :nodoc:
|
171
181
|
hash[:location] = @last_response['Location'] if @last_response['Location']
|
172
|
-
hash[:e_tag]
|
182
|
+
hash[:e_tag] = @last_response['ETag'] if @last_response['ETag']
|
173
183
|
hash
|
174
184
|
end
|
175
185
|
|
@@ -191,12 +201,12 @@ module Aws
|
|
191
201
|
#
|
192
202
|
def list_distributions
|
193
203
|
request_hash = generate_request('GET', 'distribution')
|
194
|
-
request_cache_or_info :list_distributions, request_hash,
|
204
|
+
request_cache_or_info :list_distributions, request_hash, AcfDistributionListParser, @@bench
|
195
205
|
end
|
196
206
|
|
197
207
|
def list_streaming_distributions
|
198
208
|
request_hash = generate_request('GET', 'streaming-distribution')
|
199
|
-
request_cache_or_info :list_streaming_distributions, request_hash,
|
209
|
+
request_cache_or_info :list_streaming_distributions, request_hash, AcfStreamingDistributionListParser, @@bench
|
200
210
|
end
|
201
211
|
|
202
212
|
# Create a new distribution.
|
@@ -215,27 +225,27 @@ module Aws
|
|
215
225
|
# :caller_reference => "200809102100536497863003"}
|
216
226
|
#
|
217
227
|
def create_distribution(origin, comment='', enabled=true, cnames=[], caller_reference=nil, default_root_object=nil)
|
218
|
-
body
|
228
|
+
body = distribution_config_for(origin, comment, enabled, cnames, caller_reference, false, default_root_object)
|
219
229
|
request_hash = generate_request('POST', 'distribution', body.strip)
|
220
230
|
merge_headers(request_info(request_hash, AcfDistributionParser.new))
|
221
231
|
end
|
222
232
|
|
223
233
|
def create_streaming_distribution(origin, comment='', enabled=true, cnames=[], caller_reference=nil, default_root_object=nil)
|
224
|
-
body
|
234
|
+
body = distribution_config_for(origin, comment, enabled, cnames, caller_reference, true, default_root_object)
|
225
235
|
request_hash = generate_request('POST', 'streaming-distribution', body.strip)
|
226
236
|
merge_headers(request_info(request_hash, AcfDistributionParser.new))
|
227
237
|
end
|
228
|
-
|
238
|
+
|
229
239
|
def distribution_config_for(origin, comment='', enabled=true, cnames=[], caller_reference=nil, streaming = false, default_root_object=nil)
|
230
240
|
rootElement = streaming ? "StreamingDistributionConfig" : "DistributionConfig"
|
231
241
|
# join CNAMES
|
232
|
-
cnames_str
|
242
|
+
cnames_str = ''
|
233
243
|
unless cnames.blank?
|
234
244
|
cnames.to_a.each { |cname| cnames_str += "\n <CNAME>#{cname}</CNAME>" }
|
235
245
|
end
|
236
246
|
caller_reference ||= generate_call_reference
|
237
|
-
root_ob
|
238
|
-
body
|
247
|
+
root_ob = default_root_object ? "<DefaultRootObject>#{config[:default_root_object]}</DefaultRootObject>" : ""
|
248
|
+
body = <<-EOXML
|
239
249
|
<?xml version="1.0" encoding="UTF-8"?>
|
240
250
|
<#{rootElement} xmlns=#{xmlns}>
|
241
251
|
<Origin>#{origin}</Origin>
|
@@ -307,14 +317,14 @@ module Aws
|
|
307
317
|
# acf.set_distribution_config('E2REJM3VUN5RSI', config) #=> true
|
308
318
|
#
|
309
319
|
def set_distribution_config(aws_id, config)
|
310
|
-
body
|
311
|
-
request_hash = generate_request('PUT', "distribution/#{aws_id}/config", body.strip,
|
320
|
+
body = distribution_config_for(config[:origin], config[:comment], config[:enabled], config[:cnames], config[:caller_reference], false)
|
321
|
+
request_hash = generate_request('PUT', "distribution/#{aws_id}/config", body.strip,
|
312
322
|
'If-Match' => config[:e_tag])
|
313
323
|
request_info(request_hash, RightHttp2xxParser.new)
|
314
324
|
end
|
315
325
|
|
316
326
|
def set_streaming_distribution_config(aws_id, config)
|
317
|
-
body
|
327
|
+
body = distribution_config_for(config[:origin], config[:comment], config[:enabled], config[:cnames], config[:caller_reference], true)
|
318
328
|
request_hash = generate_request('PUT', "streaming-distribution/#{aws_id}/config", body.strip,
|
319
329
|
'If-Match' => config[:e_tag])
|
320
330
|
request_info(request_hash, RightHttp2xxParser.new)
|
@@ -344,20 +354,30 @@ module Aws
|
|
344
354
|
# Parses attributes common to many CF distribution API calls
|
345
355
|
class AcfBaseDistributionParser < AwsParser # :nodoc:
|
346
356
|
def reset
|
347
|
-
@distribution = {
|
348
|
-
@result
|
357
|
+
@distribution = {:cnames => []}
|
358
|
+
@result = []
|
349
359
|
end
|
360
|
+
|
350
361
|
def tagend(name)
|
351
362
|
case name
|
352
|
-
when 'Id'
|
353
|
-
|
354
|
-
when '
|
355
|
-
|
356
|
-
when '
|
357
|
-
|
358
|
-
when '
|
359
|
-
|
360
|
-
when '
|
363
|
+
when 'Id' then
|
364
|
+
@distribution[:aws_id] = @text
|
365
|
+
when 'Status' then
|
366
|
+
@distribution[:status] = @text
|
367
|
+
when 'LastModifiedTime' then
|
368
|
+
@distribution[:last_modified_time] = Time.parse(@text)
|
369
|
+
when 'DomainName' then
|
370
|
+
@distribution[:domain_name] = @text
|
371
|
+
when 'Origin' then
|
372
|
+
@distribution[:origin] = @text
|
373
|
+
when 'CallerReference' then
|
374
|
+
@distribution[:caller_reference] = @text
|
375
|
+
when 'Comment' then
|
376
|
+
@distribution[:comment] = AcfInterface::unescape(@text)
|
377
|
+
when 'Enabled' then
|
378
|
+
@distribution[:enabled] = @text == 'true' ? true : false
|
379
|
+
when 'CNAME' then
|
380
|
+
@distribution[:cnames] << @text
|
361
381
|
end
|
362
382
|
end
|
363
383
|
end
|
@@ -371,39 +391,49 @@ module Aws
|
|
371
391
|
|
372
392
|
class AcfDistributionListParser < AcfBaseDistributionParser # :nodoc:
|
373
393
|
def tagstart(name, attributes)
|
374
|
-
@distribution = {
|
394
|
+
@distribution = {:cnames => []} if name == 'DistributionSummary'
|
375
395
|
end
|
396
|
+
|
376
397
|
def tagend(name)
|
377
398
|
super(name)
|
378
399
|
case name
|
379
|
-
when 'DistributionSummary' then
|
400
|
+
when 'DistributionSummary' then
|
401
|
+
@result << @distribution
|
380
402
|
end
|
381
403
|
end
|
382
404
|
end
|
383
405
|
|
384
406
|
class AcfDistributionConfigParser < AwsParser # :nodoc:
|
385
407
|
def reset
|
386
|
-
@result = {
|
408
|
+
@result = {:cnames => []}
|
387
409
|
end
|
410
|
+
|
388
411
|
def tagend(name)
|
389
412
|
case name
|
390
|
-
when 'Origin'
|
391
|
-
|
392
|
-
when '
|
393
|
-
|
394
|
-
when '
|
413
|
+
when 'Origin' then
|
414
|
+
@result[:origin] = @text
|
415
|
+
when 'CallerReference' then
|
416
|
+
@result[:caller_reference] = @text
|
417
|
+
when 'Comment' then
|
418
|
+
@result[:comment] = AcfInterface::unescape(@text)
|
419
|
+
when 'Enabled' then
|
420
|
+
@result[:enabled] = @text == 'true' ? true : false
|
421
|
+
when 'CNAME' then
|
422
|
+
@result[:cnames] << @text
|
395
423
|
end
|
396
424
|
end
|
397
425
|
end
|
398
426
|
|
399
427
|
class AcfStreamingDistributionListParser < AcfBaseDistributionParser # :nodoc:
|
400
428
|
def tagstart(name, attributes)
|
401
|
-
@distribution = {
|
429
|
+
@distribution = {:cnames => []} if name == 'StreamingDistributionSummary'
|
402
430
|
end
|
431
|
+
|
403
432
|
def tagend(name)
|
404
433
|
super(name)
|
405
434
|
case name
|
406
|
-
when 'StreamingDistributionSummary' then
|
435
|
+
when 'StreamingDistributionSummary' then
|
436
|
+
@result << @distribution
|
407
437
|
end
|
408
438
|
end
|
409
439
|
end
|
data/lib/aws.rb
CHANGED
@@ -15,16 +15,16 @@ require 'right_http_connection'
|
|
15
15
|
$:.unshift(File.dirname(__FILE__))
|
16
16
|
require 'awsbase/benchmark_fix'
|
17
17
|
require 'awsbase/support'
|
18
|
-
require 'awsbase/
|
18
|
+
require 'awsbase/awsbase'
|
19
19
|
require 'awsbase/aws_response_array'
|
20
|
-
require 'ec2/
|
21
|
-
require 'ec2/
|
22
|
-
require 's3/
|
23
|
-
require 's3/
|
24
|
-
require 'sqs/
|
25
|
-
require 'sqs/
|
26
|
-
require 'sdb/
|
27
|
-
require 'acf/
|
20
|
+
require 'ec2/ec2'
|
21
|
+
require 'ec2/mon_interface'
|
22
|
+
require 's3/s3_interface'
|
23
|
+
require 's3/s3'
|
24
|
+
require 'sqs/sqs_interface'
|
25
|
+
require 'sqs/sqs'
|
26
|
+
require 'sdb/sdb_interface'
|
27
|
+
require 'acf/acf_interface'
|
28
28
|
require 'elb/elb_interface'
|
29
29
|
require 'rds/rds'
|
30
30
|
require 'iam/iam'
|
@@ -1,30 +1,30 @@
|
|
1
1
|
module Aws
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
|
3
|
+
# This class is a special array to hold a bit of extra information about a response like:
|
4
|
+
# <ResponseMetadata>
|
5
|
+
# <RequestId>4f1fae46-bf3d-11de-a88b-7b5b3d23b3a7</RequestId>
|
6
|
+
# </ResponseMetadata>
|
7
|
+
#
|
8
|
+
# Which can be accessed directly from the array using array.response_metadata
|
9
|
+
#
|
10
|
+
class AwsResponseArray < Array
|
11
|
+
|
12
|
+
attr_accessor :response_metadata
|
13
|
+
|
14
|
+
def initialize(response_metadata)
|
15
|
+
@response_metadata = response_metadata
|
18
16
|
end
|
19
17
|
|
20
|
-
|
21
|
-
class AwsResponseObjectHash < Hash
|
18
|
+
end
|
22
19
|
|
23
|
-
|
20
|
+
# Used when pulling out a single response object
|
21
|
+
class AwsResponseObjectHash < Hash
|
24
22
|
|
25
|
-
|
26
|
-
@response_metadata = response_metadata
|
27
|
-
end
|
23
|
+
attr_accessor :response_metadata
|
28
24
|
|
25
|
+
def initialize(response_metadata)
|
26
|
+
@response_metadata = response_metadata
|
29
27
|
end
|
28
|
+
|
29
|
+
end
|
30
30
|
end
|
@@ -0,0 +1,579 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2007-2008 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
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#
|
23
|
+
|
24
|
+
# Test
|
25
|
+
module Aws
|
26
|
+
require 'digest/md5'
|
27
|
+
require 'pp'
|
28
|
+
require 'cgi'
|
29
|
+
require 'uri'
|
30
|
+
require 'xmlsimple'
|
31
|
+
require 'active_support/core_ext'
|
32
|
+
|
33
|
+
require_relative 'utils'
|
34
|
+
require_relative 'errors'
|
35
|
+
require_relative 'parsers'
|
36
|
+
|
37
|
+
|
38
|
+
class AwsBenchmarkingBlock #:nodoc:
|
39
|
+
attr_accessor :xml, :service
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
# Benchmark::Tms instance for service (Ec2, S3, or SQS) access benchmarking.
|
43
|
+
@service = Benchmark::Tms.new()
|
44
|
+
# Benchmark::Tms instance for XML parsing benchmarking.
|
45
|
+
@xml = Benchmark::Tms.new()
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class AwsNoChange < RuntimeError
|
50
|
+
end
|
51
|
+
|
52
|
+
class AwsBase
|
53
|
+
|
54
|
+
# Amazon HTTP Error handling
|
55
|
+
|
56
|
+
# Text, if found in an error message returned by AWS, indicates that this may be a transient
|
57
|
+
# error. Transient errors are automatically retried with exponential back-off.
|
58
|
+
AMAZON_PROBLEMS = ['internal service error',
|
59
|
+
'is currently unavailable',
|
60
|
+
'no response from',
|
61
|
+
'Please try again',
|
62
|
+
'InternalError',
|
63
|
+
'ServiceUnavailable', #from SQS docs
|
64
|
+
'Unavailable',
|
65
|
+
'This application is not currently available',
|
66
|
+
'InsufficientInstanceCapacity'
|
67
|
+
]
|
68
|
+
@@amazon_problems = AMAZON_PROBLEMS
|
69
|
+
# Returns a list of Amazon service responses which are known to be transient problems.
|
70
|
+
# We have to re-request if we get any of them, because the problem will probably disappear.
|
71
|
+
# By default this method returns the same value as the AMAZON_PROBLEMS const.
|
72
|
+
def self.amazon_problems
|
73
|
+
@@amazon_problems
|
74
|
+
end
|
75
|
+
|
76
|
+
# Sets the list of Amazon side problems. Use in conjunction with the
|
77
|
+
# getter to append problems.
|
78
|
+
def self.amazon_problems=(problems_list)
|
79
|
+
@@amazon_problems = problems_list
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
module AwsBaseInterface
|
85
|
+
|
86
|
+
DEFAULT_SIGNATURE_VERSION = '2'
|
87
|
+
|
88
|
+
module ClassMethods
|
89
|
+
|
90
|
+
def self.bench
|
91
|
+
@@bench
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.bench
|
95
|
+
@@bench
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.bench_xml
|
99
|
+
@@bench.xml
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.bench_s3
|
103
|
+
@@bench.service
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
@@caching = false
|
108
|
+
|
109
|
+
def self.caching
|
110
|
+
@@caching
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.caching=(caching)
|
114
|
+
@@caching = caching
|
115
|
+
end
|
116
|
+
|
117
|
+
# Current aws_access_key_id
|
118
|
+
attr_reader :aws_access_key_id
|
119
|
+
# Last HTTP request object
|
120
|
+
attr_reader :last_request
|
121
|
+
# Last HTTP response object
|
122
|
+
attr_reader :last_response
|
123
|
+
# Last AWS errors list (used by AWSErrorHandler)
|
124
|
+
attr_accessor :last_errors
|
125
|
+
# Last AWS request id (used by AWSErrorHandler)
|
126
|
+
attr_accessor :last_request_id
|
127
|
+
# Logger object
|
128
|
+
attr_accessor :logger
|
129
|
+
# Initial params hash
|
130
|
+
attr_accessor :params
|
131
|
+
# RightHttpConnection instance
|
132
|
+
# there's a method now to get this since it could be per thread or what have you
|
133
|
+
# attr_reader :connection
|
134
|
+
# Cache
|
135
|
+
attr_reader :cache
|
136
|
+
# Signature version (all services except s3)
|
137
|
+
attr_reader :signature_version
|
138
|
+
|
139
|
+
def init(service_info, aws_access_key_id, aws_secret_access_key, params={}) #:nodoc:
|
140
|
+
@params = params
|
141
|
+
raise AwsError.new("AWS access keys are required to operate on #{service_info[:name]}") \
|
142
|
+
if aws_access_key_id.blank? || aws_secret_access_key.blank?
|
143
|
+
@aws_access_key_id = aws_access_key_id
|
144
|
+
@aws_secret_access_key = aws_secret_access_key
|
145
|
+
# if the endpoint was explicitly defined - then use it
|
146
|
+
if @params[:endpoint_url]
|
147
|
+
@params[:server] = URI.parse(@params[:endpoint_url]).host
|
148
|
+
@params[:port] = URI.parse(@params[:endpoint_url]).port
|
149
|
+
@params[:service] = URI.parse(@params[:endpoint_url]).path
|
150
|
+
@params[:protocol] = URI.parse(@params[:endpoint_url]).scheme
|
151
|
+
@params[:region] = nil
|
152
|
+
else
|
153
|
+
@params[:server] ||= service_info[:default_host]
|
154
|
+
@params[:server] = "#{@params[:region]}.#{@params[:server]}" if @params[:region]
|
155
|
+
@params[:port] ||= service_info[:default_port]
|
156
|
+
@params[:service] ||= service_info[:default_service]
|
157
|
+
@params[:protocol] ||= service_info[:default_protocol]
|
158
|
+
@params[:api_version] ||= service_info[:api_version]
|
159
|
+
end
|
160
|
+
if !@params[:multi_thread].nil? && @params[:connection_mode].nil? # user defined this
|
161
|
+
@params[:connection_mode] = @params[:multi_thread] ? :per_thread : :single
|
162
|
+
end
|
163
|
+
# @params[:multi_thread] ||= defined?(AWS_DAEMON)
|
164
|
+
@params[:connection_mode] ||= :default
|
165
|
+
@params[:connection_mode] = :per_request if @params[:connection_mode] == :default
|
166
|
+
@logger = @params[:logger]
|
167
|
+
@logger = Rails.logger if !@logger && defined?(Rails) && defined?(Rails.logger)
|
168
|
+
@logger = ::Rails.logger if !@logger && defined?(::Rails.logger)
|
169
|
+
@logger = Logger.new(STDOUT) if !@logger
|
170
|
+
@logger.info "New #{self.class.name} using #{@params[:connection_mode].to_s}-connection mode"
|
171
|
+
@error_handler = nil
|
172
|
+
@cache = {}
|
173
|
+
@signature_version = (params[:signature_version] || DEFAULT_SIGNATURE_VERSION).to_s
|
174
|
+
end
|
175
|
+
|
176
|
+
def signed_service_params(aws_secret_access_key, service_hash, http_verb=nil, host=nil, service=nil)
|
177
|
+
case signature_version.to_s
|
178
|
+
when '0' then
|
179
|
+
AwsUtils::sign_request_v0(aws_secret_access_key, service_hash)
|
180
|
+
when '1' then
|
181
|
+
AwsUtils::sign_request_v1(aws_secret_access_key, service_hash)
|
182
|
+
when '2' then
|
183
|
+
AwsUtils::sign_request_v2(aws_secret_access_key, service_hash, http_verb, host, service)
|
184
|
+
else
|
185
|
+
raise AwsError.new("Unknown signature version (#{signature_version.to_s}) requested")
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def generate_request(action, params={})
|
190
|
+
generate_request2(@aws_access_key_id, @aws_secret_access_key, action, @params[:api_version], @params, params)
|
191
|
+
end
|
192
|
+
|
193
|
+
# FROM SDB
|
194
|
+
def generate_request2(aws_access_key, aws_secret_key, action, api_version, lib_params, user_params={}, options={}) #:nodoc:
|
195
|
+
# remove empty params from request
|
196
|
+
user_params.delete_if { |key, value| value.nil? }
|
197
|
+
# user_params.each_pair do |k,v|
|
198
|
+
# user_params[k] = v.force_encoding("UTF-8")
|
199
|
+
# end
|
200
|
+
#params_string = params.to_a.collect{|key,val| key + "=#{CGI::escape(val.to_s)}" }.join("&")
|
201
|
+
# prepare service data
|
202
|
+
service = lib_params[:service]
|
203
|
+
# puts 'service=' + service.to_s
|
204
|
+
service_hash = {"Action" => action,
|
205
|
+
"AWSAccessKeyId" => aws_access_key}
|
206
|
+
service_hash.update("Version" => api_version) if api_version
|
207
|
+
service_hash.update(user_params)
|
208
|
+
service_params = signed_service_params(aws_secret_key, service_hash, :get, lib_params[:server], lib_params[:service])
|
209
|
+
#
|
210
|
+
# use POST method if the length of the query string is too large
|
211
|
+
# see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/MakingRESTRequests.html
|
212
|
+
if service_params.size > 2000
|
213
|
+
if signature_version == '2'
|
214
|
+
# resign the request because HTTP verb is included into signature
|
215
|
+
service_params = signed_service_params(aws_secret_key, service_hash, :post, lib_params[:server], service)
|
216
|
+
end
|
217
|
+
request = Net::HTTP::Post.new(service)
|
218
|
+
request.body = service_params
|
219
|
+
request['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'
|
220
|
+
else
|
221
|
+
request = Net::HTTP::Get.new("#{service}?#{service_params}")
|
222
|
+
end
|
223
|
+
|
224
|
+
#puts "\n\n --------------- QUERY REQUEST TO AWS -------------- \n\n"
|
225
|
+
#puts "#{@params[:service]}?#{service_params}\n\n"
|
226
|
+
|
227
|
+
# prepare output hash
|
228
|
+
{:request => request,
|
229
|
+
:server => lib_params[:server],
|
230
|
+
:port => lib_params[:port],
|
231
|
+
:protocol => lib_params[:protocol]}
|
232
|
+
end
|
233
|
+
|
234
|
+
def get_conn(connection_name, lib_params, logger)
|
235
|
+
# thread = lib_params[:multi_thread] ? Thread.current : Thread.main
|
236
|
+
# thread[connection_name] ||= Rightscale::HttpConnection.new(:exception => Aws::AwsError, :logger => logger)
|
237
|
+
# conn = thread[connection_name]
|
238
|
+
# return conn
|
239
|
+
http_conn = nil
|
240
|
+
conn_mode = lib_params[:connection_mode]
|
241
|
+
|
242
|
+
# Slice all parameters accepted by Rightscale::HttpConnection#new
|
243
|
+
params = lib_params.slice(
|
244
|
+
:user_agent, :ca_file, :http_connection_retry_count, :http_connection_open_timeout,
|
245
|
+
:http_connection_read_timeout, :http_connection_retry_delay
|
246
|
+
)
|
247
|
+
params.merge!(:exception => AwsError, :logger => logger)
|
248
|
+
|
249
|
+
if conn_mode == :per_request
|
250
|
+
http_conn = Rightscale::HttpConnection.new(params)
|
251
|
+
|
252
|
+
elsif conn_mode == :per_thread || conn_mode == :single
|
253
|
+
thread = conn_mode == :per_thread ? Thread.current : Thread.main
|
254
|
+
thread[connection_name] ||= Rightscale::HttpConnection.new(params)
|
255
|
+
http_conn = thread[connection_name]
|
256
|
+
# ret = request_info_impl(http_conn, bench, request, parser, &block)
|
257
|
+
end
|
258
|
+
return http_conn
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
def close_conn(conn_name)
|
263
|
+
conn_mode = @params[:connection_mode]
|
264
|
+
if conn_mode == :per_thread || conn_mode == :single
|
265
|
+
thread = conn_mode == :per_thread ? Thread.current : Thread.main
|
266
|
+
if !thread[conn_name].nil?
|
267
|
+
thread[conn_name].finish
|
268
|
+
thread[conn_name] = nil
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def connection
|
274
|
+
get_conn(self.class.connection_name, self.params, self.logger)
|
275
|
+
end
|
276
|
+
|
277
|
+
def close_connection
|
278
|
+
close_conn(self.class.connection_name)
|
279
|
+
end
|
280
|
+
|
281
|
+
|
282
|
+
def request_info2(request, parser, lib_params, connection_name, logger, bench, options={}, &block) #:nodoc:
|
283
|
+
ret = nil
|
284
|
+
# puts 'OPTIONS=' + options.inspect
|
285
|
+
http_conn = get_conn(connection_name, lib_params, logger)
|
286
|
+
begin
|
287
|
+
# todo: this QueryTimeout retry should go into a SimpleDbErrorHandler, not here
|
288
|
+
retry_count = 1
|
289
|
+
count = 0
|
290
|
+
while count <= retry_count
|
291
|
+
puts 'RETRYING QUERY due to QueryTimeout...' if count > 0
|
292
|
+
begin
|
293
|
+
ret = request_info_impl(http_conn, bench, request, parser, options, &block)
|
294
|
+
break
|
295
|
+
rescue Aws::AwsError => ex
|
296
|
+
if !ex.include?(/QueryTimeout/) || count == retry_count
|
297
|
+
raise ex
|
298
|
+
end
|
299
|
+
end
|
300
|
+
count += 1
|
301
|
+
end
|
302
|
+
ensure
|
303
|
+
http_conn.finish if http_conn && lib_params[:connection_mode] == :per_request
|
304
|
+
end
|
305
|
+
ret
|
306
|
+
end
|
307
|
+
|
308
|
+
# This is the latest and greatest now. Service must have connection_name defined.
|
309
|
+
def request_info3(service_interface, request, parser, options, &block)
|
310
|
+
request_info2(request, parser,
|
311
|
+
service_interface.params,
|
312
|
+
service_interface.class.connection_name,
|
313
|
+
service_interface.logger,
|
314
|
+
service_interface.class.bench,
|
315
|
+
options, &block)
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
# This is the direction we should head instead of writing our own parsers for everything, much simpler
|
320
|
+
# params:
|
321
|
+
# - :group_tags => hash of indirection to eliminate, see: http://xml-simple.rubyforge.org/
|
322
|
+
# - :force_array => true for all or an array of tag names to force
|
323
|
+
# - :pull_out_array => an array of levels to dig into when generating return value (see rds.rb for example)
|
324
|
+
def request_info_xml_simple(connection_name, lib_params, request, logger, params = {})
|
325
|
+
|
326
|
+
@connection = get_conn(connection_name, lib_params, logger)
|
327
|
+
begin
|
328
|
+
@last_request = request[:request]
|
329
|
+
@last_response = nil
|
330
|
+
|
331
|
+
response = @connection.request(request)
|
332
|
+
# puts "response=" + response.body
|
333
|
+
# benchblock.service.add!{ response = @connection.request(request) }
|
334
|
+
# check response for errors...
|
335
|
+
@last_response = response
|
336
|
+
if response.is_a?(Net::HTTPSuccess)
|
337
|
+
@error_handler = nil
|
338
|
+
# benchblock.xml.add! { parser.parse(response) }
|
339
|
+
# return parser.result
|
340
|
+
force_array = params[:force_array] || false
|
341
|
+
# Force_array and group_tags don't work nice together so going to force array manually
|
342
|
+
xml_simple_options = {"KeyToSymbol"=>false, 'ForceArray' => false}
|
343
|
+
xml_simple_options["GroupTags"] = params[:group_tags] if params[:group_tags]
|
344
|
+
|
345
|
+
# { 'GroupTags' => { 'searchpath' => 'dir' }
|
346
|
+
# 'ForceArray' => %r(_list$)
|
347
|
+
parsed = XmlSimple.xml_in(response.body, xml_simple_options)
|
348
|
+
# todo: we may want to consider stripping off a couple of layers when doing this, for instance:
|
349
|
+
# <DescribeDBInstancesResponse xmlns="http://rds.amazonaws.com/admin/2009-10-16/">
|
350
|
+
# <DescribeDBInstancesResult>
|
351
|
+
# <DBInstances>
|
352
|
+
# <DBInstance>....
|
353
|
+
# Strip it off and only return an array or hash of <DBInstance>'s (hash by identifier).
|
354
|
+
# would have to be able to make the RequestId available somehow though, perhaps some special array subclass which included that?
|
355
|
+
unless force_array.is_a? Array
|
356
|
+
force_array = []
|
357
|
+
end
|
358
|
+
parsed = symbolize(parsed, force_array)
|
359
|
+
# puts 'parsed=' + parsed.inspect
|
360
|
+
if params[:pull_out_array]
|
361
|
+
ret = Aws::AwsResponseArray.new(parsed[:response_metadata])
|
362
|
+
level_hash = parsed
|
363
|
+
params[:pull_out_array].each do |x|
|
364
|
+
level_hash = level_hash[x]
|
365
|
+
end
|
366
|
+
if level_hash.is_a? Hash # When there's only one
|
367
|
+
ret << level_hash
|
368
|
+
else # should be array
|
369
|
+
# puts 'level_hash=' + level_hash.inspect
|
370
|
+
level_hash.each do |x|
|
371
|
+
ret << x
|
372
|
+
end
|
373
|
+
end
|
374
|
+
elsif params[:pull_out_single]
|
375
|
+
# returns a single object
|
376
|
+
ret = AwsResponseObjectHash.new(parsed[:response_metadata])
|
377
|
+
level_hash = parsed
|
378
|
+
params[:pull_out_single].each do |x|
|
379
|
+
level_hash = level_hash[x]
|
380
|
+
end
|
381
|
+
ret.merge!(level_hash)
|
382
|
+
else
|
383
|
+
ret = parsed
|
384
|
+
end
|
385
|
+
return ret
|
386
|
+
|
387
|
+
else
|
388
|
+
@error_handler = AWSErrorHandler.new(self, nil, :errors_list => self.class.amazon_problems) unless @error_handler
|
389
|
+
check_result = @error_handler.check(request)
|
390
|
+
if check_result
|
391
|
+
@error_handler = nil
|
392
|
+
return check_result
|
393
|
+
end
|
394
|
+
request_text_data = "#{request[:server]}:#{request[:port]}#{request[:request].path}"
|
395
|
+
raise AwsError2.new(@last_response.code, @last_request_id, request_text_data, @last_response.body)
|
396
|
+
end
|
397
|
+
ensure
|
398
|
+
@connection.finish if @connection && lib_params[:connection_mode] == :per_request
|
399
|
+
end
|
400
|
+
|
401
|
+
end
|
402
|
+
|
403
|
+
def symbolize(hash, force_array)
|
404
|
+
ret = {}
|
405
|
+
hash.keys.each do |key|
|
406
|
+
val = hash[key]
|
407
|
+
if val.is_a? Hash
|
408
|
+
val = symbolize(val, force_array)
|
409
|
+
if force_array.include? key
|
410
|
+
val = [val]
|
411
|
+
end
|
412
|
+
elsif val.is_a? Array
|
413
|
+
val = val.collect { |x| symbolize(x, force_array) }
|
414
|
+
end
|
415
|
+
ret[key.underscore.to_sym] = val
|
416
|
+
end
|
417
|
+
ret
|
418
|
+
end
|
419
|
+
|
420
|
+
# Returns +true+ if the describe_xxx responses are being cached
|
421
|
+
def caching?
|
422
|
+
@params.key?(:cache) ? @params[:cache] : @@caching
|
423
|
+
end
|
424
|
+
|
425
|
+
# Check if the aws function response hits the cache or not.
|
426
|
+
# If the cache hits:
|
427
|
+
# - raises an +AwsNoChange+ exception if +do_raise+ == +:raise+.
|
428
|
+
# - returnes parsed response from the cache if it exists or +true+ otherwise.
|
429
|
+
# If the cache miss or the caching is off then returns +false+.
|
430
|
+
def cache_hits?(function, response, do_raise=:raise)
|
431
|
+
result = false
|
432
|
+
if caching?
|
433
|
+
function = function.to_sym
|
434
|
+
# get rid of requestId (this bad boy was added for API 2008-08-08+ and it is uniq for every response)
|
435
|
+
response = response.sub(%r{<requestId>.+?</requestId>}, '')
|
436
|
+
response_md5 =Digest::MD5.hexdigest(response).to_s
|
437
|
+
# check for changes
|
438
|
+
unless @cache[function] && @cache[function][:response_md5] == response_md5
|
439
|
+
# well, the response is new, reset cache data
|
440
|
+
update_cache(function, {:response_md5 => response_md5,
|
441
|
+
:timestamp => Time.now,
|
442
|
+
:hits => 0,
|
443
|
+
:parsed => nil})
|
444
|
+
else
|
445
|
+
# aha, cache hits, update the data and throw an exception if needed
|
446
|
+
@cache[function][:hits] += 1
|
447
|
+
if do_raise == :raise
|
448
|
+
raise(AwsNoChange, "Cache hit: #{function} response has not changed since "+
|
449
|
+
"#{@cache[function][:timestamp].strftime('%Y-%m-%d %H:%M:%S')}, "+
|
450
|
+
"hits: #{@cache[function][:hits]}.")
|
451
|
+
else
|
452
|
+
result = @cache[function][:parsed] || true
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
456
|
+
result
|
457
|
+
end
|
458
|
+
|
459
|
+
def update_cache(function, hash)
|
460
|
+
(@cache[function.to_sym] ||= {}).merge!(hash) if caching?
|
461
|
+
end
|
462
|
+
|
463
|
+
def on_exception(options={:raise=>true, :log=>true}) # :nodoc:
|
464
|
+
raise if $!.is_a?(AwsNoChange)
|
465
|
+
AwsError::on_aws_exception(self, options)
|
466
|
+
end
|
467
|
+
|
468
|
+
# Return +true+ if this instance works in multi_thread mode and +false+ otherwise.
|
469
|
+
def multi_thread
|
470
|
+
@params[:multi_thread]
|
471
|
+
end
|
472
|
+
|
473
|
+
|
474
|
+
def request_info_impl(connection, benchblock, request, parser, options={}, &block) #:nodoc:
|
475
|
+
@connection = connection
|
476
|
+
@last_request = request[:request]
|
477
|
+
@last_response = nil
|
478
|
+
response =nil
|
479
|
+
blockexception = nil
|
480
|
+
|
481
|
+
# puts 'OPTIONS2=' + options.inspect
|
482
|
+
|
483
|
+
if (block != nil)
|
484
|
+
# TRB 9/17/07 Careful - because we are passing in blocks, we get a situation where
|
485
|
+
# an exception may get thrown in the block body (which is high-level
|
486
|
+
# code either here or in the application) but gets caught in the
|
487
|
+
# low-level code of HttpConnection. The solution is not to let any
|
488
|
+
# exception escape the block that we pass to HttpConnection::request.
|
489
|
+
# Exceptions can originate from code directly in the block, or from user
|
490
|
+
# code called in the other block which is passed to response.read_body.
|
491
|
+
benchblock.service.add! do
|
492
|
+
responsehdr = @connection.request(request) do |response|
|
493
|
+
#########
|
494
|
+
begin
|
495
|
+
@last_response = response
|
496
|
+
if response.is_a?(Net::HTTPSuccess)
|
497
|
+
@error_handler = nil
|
498
|
+
response.read_body(&block)
|
499
|
+
else
|
500
|
+
@error_handler = AWSErrorHandler.new(self, parser, :errors_list => self.class.amazon_problems) unless @error_handler
|
501
|
+
check_result = @error_handler.check(request, options)
|
502
|
+
if check_result
|
503
|
+
@error_handler = nil
|
504
|
+
return check_result
|
505
|
+
end
|
506
|
+
request_text_data = "#{request[:server]}:#{request[:port]}#{request[:request].path}"
|
507
|
+
raise AwsError.new(@last_errors, @last_response.code, @last_request_id, request_text_data)
|
508
|
+
end
|
509
|
+
rescue Exception => e
|
510
|
+
blockexception = e
|
511
|
+
end
|
512
|
+
end
|
513
|
+
#########
|
514
|
+
|
515
|
+
#OK, now we are out of the block passed to the lower level
|
516
|
+
if (blockexception)
|
517
|
+
raise blockexception
|
518
|
+
end
|
519
|
+
benchblock.xml.add! do
|
520
|
+
parser.parse(responsehdr)
|
521
|
+
end
|
522
|
+
return parser.result
|
523
|
+
end
|
524
|
+
else
|
525
|
+
benchblock.service.add! { response = @connection.request(request) }
|
526
|
+
# check response for errors...
|
527
|
+
@last_response = response
|
528
|
+
if response.is_a?(Net::HTTPSuccess)
|
529
|
+
@error_handler = nil
|
530
|
+
benchblock.xml.add! { parser.parse(response) }
|
531
|
+
return parser.result
|
532
|
+
else
|
533
|
+
@error_handler = AWSErrorHandler.new(self, parser, :errors_list => self.class.amazon_problems) unless @error_handler
|
534
|
+
check_result = @error_handler.check(request, options)
|
535
|
+
if check_result
|
536
|
+
@error_handler = nil
|
537
|
+
return check_result
|
538
|
+
end
|
539
|
+
request_text_data = "#{request[:server]}:#{request[:port]}#{request[:request].path}"
|
540
|
+
raise AwsError.new(@last_errors, @last_response.code, @last_request_id, request_text_data)
|
541
|
+
end
|
542
|
+
end
|
543
|
+
rescue
|
544
|
+
@error_handler = nil
|
545
|
+
raise
|
546
|
+
end
|
547
|
+
|
548
|
+
def request_cache_or_info(method, link, parser_class, benchblock, use_cache=true) #:nodoc:
|
549
|
+
# We do not want to break the logic of parsing hence will use a dummy parser to process all the standard
|
550
|
+
# steps (errors checking etc). The dummy parser does nothig - just returns back the params it received.
|
551
|
+
# If the caching is enabled and hit then throw AwsNoChange.
|
552
|
+
# P.S. caching works for the whole images list only! (when the list param is blank)
|
553
|
+
# check cache
|
554
|
+
response, params = request_info(link, RightDummyParser.new)
|
555
|
+
cache_hits?(method.to_sym, response.body) if use_cache
|
556
|
+
parser = parser_class.new(:logger => @logger)
|
557
|
+
benchblock.xml.add! { parser.parse(response, params) }
|
558
|
+
result = block_given? ? yield(parser) : parser.result
|
559
|
+
# update parsed data
|
560
|
+
update_cache(method.to_sym, :parsed => result) if use_cache
|
561
|
+
result
|
562
|
+
end
|
563
|
+
|
564
|
+
# Returns Amazons request ID for the latest request
|
565
|
+
def last_request_id
|
566
|
+
@last_response && @last_response.body.to_s[%r{<requestId>(.+?)</requestId>}] && $1
|
567
|
+
end
|
568
|
+
|
569
|
+
def hash_params(prefix, list) #:nodoc:
|
570
|
+
groups = {}
|
571
|
+
list.each_index { |i| groups.update("#{prefix}.#{i+1}"=>list[i]) } if list
|
572
|
+
return groups
|
573
|
+
end
|
574
|
+
|
575
|
+
end
|
576
|
+
|
577
|
+
|
578
|
+
end
|
579
|
+
|