gotime_aws 2.5.6
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/README.markdown +119 -0
- data/lib/acf/acf_interface.rb +444 -0
- data/lib/awsbase/aws_response_array.rb +30 -0
- data/lib/awsbase/awsbase.rb +611 -0
- data/lib/awsbase/benchmark_fix.rb +39 -0
- data/lib/awsbase/errors.rb +296 -0
- data/lib/awsbase/parsers.rb +227 -0
- data/lib/awsbase/require_relative.rb +20 -0
- data/lib/awsbase/utils.rb +255 -0
- data/lib/ec2/ec2.rb +2294 -0
- data/lib/ec2/mon_interface.rb +244 -0
- data/lib/elb/elb_interface.rb +366 -0
- data/lib/gotime_aws.rb +33 -0
- data/lib/iam/iam.rb +126 -0
- data/lib/rds/rds.rb +222 -0
- data/lib/right_aws.rb +57 -0
- data/lib/s3/bucket.rb +278 -0
- data/lib/s3/grantee.rb +238 -0
- data/lib/s3/key.rb +281 -0
- data/lib/s3/s3.rb +347 -0
- data/lib/s3/s3_interface.rb +1279 -0
- data/lib/sdb/active_sdb.rb +989 -0
- data/lib/sdb/sdb_interface.rb +890 -0
- data/lib/ses/ses.rb +123 -0
- data/lib/sqs/sqs.rb +307 -0
- data/lib/sqs/sqs_interface.rb +483 -0
- metadata +107 -0
@@ -0,0 +1,611 @@
|
|
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 'net/http'
|
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_xml
|
95
|
+
@@bench.xml
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.bench_s3
|
99
|
+
@@bench.service
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
@@caching = false
|
104
|
+
|
105
|
+
def self.caching
|
106
|
+
@@caching
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.caching=(caching)
|
110
|
+
@@caching = caching
|
111
|
+
end
|
112
|
+
|
113
|
+
# Current aws_access_key_id
|
114
|
+
attr_reader :aws_access_key_id
|
115
|
+
# Last HTTP request object
|
116
|
+
attr_reader :last_request
|
117
|
+
# Last HTTP response object
|
118
|
+
attr_reader :last_response
|
119
|
+
# Last AWS errors list (used by AWSErrorHandler)
|
120
|
+
attr_accessor :last_errors
|
121
|
+
# Last AWS request id (used by AWSErrorHandler)
|
122
|
+
attr_accessor :last_request_id
|
123
|
+
# Logger object
|
124
|
+
attr_accessor :logger
|
125
|
+
# Initial params hash
|
126
|
+
attr_accessor :params
|
127
|
+
# RightHttpConnection instance
|
128
|
+
# there's a method now to get this since it could be per thread or what have you
|
129
|
+
# attr_reader :connection
|
130
|
+
# Cache
|
131
|
+
attr_reader :cache
|
132
|
+
# Signature version (all services except s3)
|
133
|
+
attr_reader :signature_version
|
134
|
+
|
135
|
+
def init(service_info, aws_access_key_id, aws_secret_access_key, params={}) #:nodoc:
|
136
|
+
@params = params
|
137
|
+
if Aws::Utils.blank?(aws_access_key_id) || Aws::Utils.blank?(aws_secret_access_key)
|
138
|
+
raise AwsError.new("AWS access keys are required to operate on #{service_info[:name]}")
|
139
|
+
end
|
140
|
+
@aws_access_key_id = aws_access_key_id
|
141
|
+
@aws_secret_access_key = aws_secret_access_key
|
142
|
+
# if the endpoint was explicitly defined - then use it
|
143
|
+
if @params[:endpoint_url]
|
144
|
+
@params[:server] = URI.parse(@params[:endpoint_url]).host
|
145
|
+
@params[:port] = URI.parse(@params[:endpoint_url]).port
|
146
|
+
@params[:service] = URI.parse(@params[:endpoint_url]).path
|
147
|
+
@params[:protocol] = URI.parse(@params[:endpoint_url]).scheme
|
148
|
+
@params[:region] = nil
|
149
|
+
else
|
150
|
+
@params[:server] ||= service_info[:default_host]
|
151
|
+
@params[:server] = "#{@params[:region]}.#{@params[:server]}" if @params[:region]
|
152
|
+
@params[:port] ||= service_info[:default_port]
|
153
|
+
@params[:service] ||= service_info[:default_service]
|
154
|
+
@params[:protocol] ||= service_info[:default_protocol]
|
155
|
+
@params[:api_version] ||= service_info[:api_version]
|
156
|
+
end
|
157
|
+
if !@params[:multi_thread].nil? && @params[:connection_mode].nil? # user defined this
|
158
|
+
@params[:connection_mode] = @params[:multi_thread] ? :per_thread : :single
|
159
|
+
end
|
160
|
+
# @params[:multi_thread] ||= defined?(AWS_DAEMON)
|
161
|
+
@params[:connection_mode] ||= :default
|
162
|
+
@params[:connection_mode] = :per_request if @params[:connection_mode] == :default
|
163
|
+
@logger = @params[:logger]
|
164
|
+
@logger = Rails.logger if !@logger && defined?(Rails) && defined?(Rails.logger)
|
165
|
+
@logger = ::Rails.logger if !@logger && defined?(::Rails.logger)
|
166
|
+
@logger = Logger.new(STDOUT) if !@logger
|
167
|
+
@logger.info "New #{self.class.name} using #{@params[:connection_mode].to_s}-connection mode"
|
168
|
+
@error_handler = nil
|
169
|
+
@cache = {}
|
170
|
+
@signature_version = (params[:signature_version] || service_info[:signature_version] || DEFAULT_SIGNATURE_VERSION).to_s
|
171
|
+
end
|
172
|
+
|
173
|
+
def signed_service_params(aws_secret_access_key, service_hash, http_verb=nil, host=nil, service=nil)
|
174
|
+
case signature_version.to_s
|
175
|
+
when '0' then
|
176
|
+
Utils::sign_request_v0(aws_secret_access_key, service_hash)
|
177
|
+
when '1' then
|
178
|
+
Utils::sign_request_v1(aws_secret_access_key, service_hash)
|
179
|
+
when '2' then
|
180
|
+
Utils::sign_request_v2(aws_secret_access_key, service_hash, http_verb, host, service)
|
181
|
+
else
|
182
|
+
raise AwsError.new("Unknown signature version (#{signature_version.to_s}) requested")
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def generate_request(action, params={})
|
187
|
+
generate_request2(@aws_access_key_id, @aws_secret_access_key, action, @params[:api_version], @params, params)
|
188
|
+
end
|
189
|
+
|
190
|
+
# FROM SDB
|
191
|
+
def generate_request2(aws_access_key, aws_secret_key, action, api_version, lib_params, user_params={}, options={}) #:nodoc:
|
192
|
+
# remove empty params from request
|
193
|
+
user_params.delete_if { |key, value| value.nil? }
|
194
|
+
# user_params.each_pair do |k,v|
|
195
|
+
# user_params[k] = v.force_encoding("UTF-8")
|
196
|
+
# end
|
197
|
+
#params_string = params.to_a.collect{|key,val| key + "=#{CGI::escape(val.to_s)}" }.join("&")
|
198
|
+
# prepare service data
|
199
|
+
service = lib_params[:service]
|
200
|
+
|
201
|
+
now = Time.now.getutc
|
202
|
+
service_hash = {"Action" => action,
|
203
|
+
"AWSAccessKeyId" => aws_access_key}
|
204
|
+
service_hash.update("Version" => api_version) if api_version
|
205
|
+
service_hash.update(user_params)
|
206
|
+
headers = {}
|
207
|
+
if signature_version == '3'
|
208
|
+
service_hash["Timestamp"] = now.iso8601
|
209
|
+
service_params = escape_params(service_hash)
|
210
|
+
signature, algorithm = Aws::Utils.signature_version3(aws_secret_key, now)
|
211
|
+
headers['X-Amzn-Authorization'] = "AWS3-HTTPS AWSAccessKeyId=#{aws_access_key}, Algorithm=#{algorithm.upcase}, Signature=#{signature}"
|
212
|
+
headers['Date'] = now.httpdate
|
213
|
+
else
|
214
|
+
# puts 'service=' + service.to_s
|
215
|
+
service_params = signed_service_params(aws_secret_key, service_hash, :get, lib_params[:server], lib_params[:service])
|
216
|
+
end
|
217
|
+
|
218
|
+
#
|
219
|
+
# use POST method if the length of the query string is too large
|
220
|
+
# see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/MakingRESTRequests.html
|
221
|
+
if service_params.size > 2000
|
222
|
+
if signature_version == '2'
|
223
|
+
# resign the request because HTTP verb is included into signature
|
224
|
+
service_params = signed_service_params(aws_secret_key, service_hash, :post, lib_params[:server], service)
|
225
|
+
end
|
226
|
+
request = Net::HTTP::Post.new(service)
|
227
|
+
request.body = service_params
|
228
|
+
request['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'
|
229
|
+
else
|
230
|
+
request = Net::HTTP::Get.new("#{service}?#{service_params}")
|
231
|
+
end
|
232
|
+
headers.each_pair do |k, v|
|
233
|
+
request[k] = v
|
234
|
+
end
|
235
|
+
# puts "header=" + request['X-Amzn-Authorization']
|
236
|
+
|
237
|
+
#puts "\n\n --------------- QUERY REQUEST TO AWS -------------- \n\n"
|
238
|
+
#puts "#{@params[:service]}?#{service_params}\n\n"
|
239
|
+
|
240
|
+
# prepare output hash
|
241
|
+
{:request => request,
|
242
|
+
:server => lib_params[:server],
|
243
|
+
:port => lib_params[:port],
|
244
|
+
:protocol => lib_params[:protocol]}
|
245
|
+
end
|
246
|
+
|
247
|
+
def escape_params(service_hash)
|
248
|
+
canonical_string = service_hash.keys.sort.map do |key|
|
249
|
+
"#{Aws::Utils.amz_escape(key)}=#{Aws::Utils.amz_escape(service_hash[key])}"
|
250
|
+
end.join('&')
|
251
|
+
canonical_string
|
252
|
+
end
|
253
|
+
|
254
|
+
def get_conn(connection_name, lib_params, logger)
|
255
|
+
# thread = lib_params[:multi_thread] ? Thread.current : Thread.main
|
256
|
+
# thread[connection_name] ||= Rightscale::HttpConnection.new(:exception => Aws::AwsError, :logger => logger)
|
257
|
+
# conn = thread[connection_name]
|
258
|
+
# return conn
|
259
|
+
http_conn = nil
|
260
|
+
conn_mode = lib_params[:connection_mode]
|
261
|
+
|
262
|
+
params = { :exception => AwsError, :logger => logger }
|
263
|
+
|
264
|
+
# Adds all parameters accepted by Rightscale::HttpConnection#new
|
265
|
+
[ :user_agent, :ca_file, :http_connection_retry_count,
|
266
|
+
:http_connection_open_timeout, :http_connection_read_timeout,
|
267
|
+
:http_connection_retry_delay
|
268
|
+
].each do |key|
|
269
|
+
params[key] = lib_params[key] if lib_params.has_key?(key)
|
270
|
+
end
|
271
|
+
|
272
|
+
if conn_mode == :per_request
|
273
|
+
http_conn = Rightscale::HttpConnection.new(params)
|
274
|
+
|
275
|
+
elsif conn_mode == :per_thread || conn_mode == :single
|
276
|
+
thread = conn_mode == :per_thread ? Thread.current : Thread.main
|
277
|
+
thread[connection_name] ||= Rightscale::HttpConnection.new(params)
|
278
|
+
http_conn = thread[connection_name]
|
279
|
+
# ret = request_info_impl(http_conn, bench, request, parser, &block)
|
280
|
+
end
|
281
|
+
return http_conn
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
def close_conn(conn_name)
|
286
|
+
conn_mode = @params[:connection_mode]
|
287
|
+
if conn_mode == :per_thread || conn_mode == :single
|
288
|
+
thread = conn_mode == :per_thread ? Thread.current : Thread.main
|
289
|
+
if !thread[conn_name].nil?
|
290
|
+
thread[conn_name].finish
|
291
|
+
thread[conn_name] = nil
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def connection
|
297
|
+
get_conn(self.class.connection_name, self.params, self.logger)
|
298
|
+
end
|
299
|
+
|
300
|
+
def close_connection
|
301
|
+
close_conn(self.class.connection_name)
|
302
|
+
end
|
303
|
+
|
304
|
+
|
305
|
+
def request_info2(request, parser, lib_params, connection_name, logger, bench, options={}, &block) #:nodoc:
|
306
|
+
ret = nil
|
307
|
+
# puts 'OPTIONS=' + options.inspect
|
308
|
+
http_conn = get_conn(connection_name, lib_params, logger)
|
309
|
+
begin
|
310
|
+
# todo: this QueryTimeout retry should go into a SimpleDbErrorHandler, not here
|
311
|
+
retry_count = 1
|
312
|
+
count = 0
|
313
|
+
while count <= retry_count
|
314
|
+
puts 'RETRYING QUERY due to QueryTimeout...' if count > 0
|
315
|
+
begin
|
316
|
+
ret = request_info_impl(http_conn, bench, request, parser, options, &block)
|
317
|
+
rescue Aws::AwsError => ex
|
318
|
+
if !ex.include?(/QueryTimeout/) || count == retry_count
|
319
|
+
raise ex
|
320
|
+
end
|
321
|
+
end
|
322
|
+
break if ret
|
323
|
+
count += 1
|
324
|
+
end
|
325
|
+
ensure
|
326
|
+
http_conn.finish if http_conn && lib_params[:connection_mode] == :per_request
|
327
|
+
end
|
328
|
+
ret
|
329
|
+
end
|
330
|
+
|
331
|
+
# This is the latest and greatest now. Service must have connection_name defined.
|
332
|
+
def request_info3(service_interface, request, parser, options, &block)
|
333
|
+
request_info2(request, parser,
|
334
|
+
service_interface.params,
|
335
|
+
service_interface.class.connection_name,
|
336
|
+
service_interface.logger,
|
337
|
+
service_interface.class.bench,
|
338
|
+
options, &block)
|
339
|
+
end
|
340
|
+
|
341
|
+
|
342
|
+
# This is the direction we should head instead of writing our own parsers for everything, much simpler
|
343
|
+
# params:
|
344
|
+
# - :group_tags => hash of indirection to eliminate, see: http://xml-simple.rubyforge.org/
|
345
|
+
# - :force_array => true for all or an array of tag names to force
|
346
|
+
# - :pull_out_array => an array of levels to dig into when generating return value (see rds.rb for example)
|
347
|
+
def request_info_xml_simple(connection_name, lib_params, request, logger, params = {})
|
348
|
+
|
349
|
+
connection = get_conn(connection_name, lib_params, logger)
|
350
|
+
begin
|
351
|
+
@last_request = request[:request]
|
352
|
+
@last_response = nil
|
353
|
+
|
354
|
+
response = connection.request(request)
|
355
|
+
# puts "response=" + response.body
|
356
|
+
# benchblock.service.add!{ response = connection.request(request) }
|
357
|
+
# check response for errors...
|
358
|
+
@last_response = response
|
359
|
+
if response.is_a?(Net::HTTPSuccess)
|
360
|
+
@error_handler = nil
|
361
|
+
# benchblock.xml.add! { parser.parse(response) }
|
362
|
+
# return parser.result
|
363
|
+
force_array = params[:force_array] || false
|
364
|
+
# Force_array and group_tags don't work nice together so going to force array manually
|
365
|
+
xml_simple_options = {"KeyToSymbol"=>false, 'ForceArray' => false}
|
366
|
+
xml_simple_options["GroupTags"] = params[:group_tags] if params[:group_tags]
|
367
|
+
|
368
|
+
# { 'GroupTags' => { 'searchpath' => 'dir' }
|
369
|
+
# 'ForceArray' => %r(_list$)
|
370
|
+
parsed = XmlSimple.xml_in(response.body, xml_simple_options)
|
371
|
+
# todo: we may want to consider stripping off a couple of layers when doing this, for instance:
|
372
|
+
# <DescribeDBInstancesResponse xmlns="http://rds.amazonaws.com/admin/2009-10-16/">
|
373
|
+
# <DescribeDBInstancesResult>
|
374
|
+
# <DBInstances>
|
375
|
+
# <DBInstance>....
|
376
|
+
# Strip it off and only return an array or hash of <DBInstance>'s (hash by identifier).
|
377
|
+
# would have to be able to make the RequestId available somehow though, perhaps some special array subclass which included that?
|
378
|
+
unless force_array.is_a? Array
|
379
|
+
force_array = []
|
380
|
+
end
|
381
|
+
parsed = symbolize(parsed, force_array)
|
382
|
+
# puts 'parsed=' + parsed.inspect
|
383
|
+
if params[:pull_out_array]
|
384
|
+
ret = Aws::AwsResponseArray.new(parsed[:response_metadata])
|
385
|
+
level_hash = parsed
|
386
|
+
params[:pull_out_array].each do |x|
|
387
|
+
level_hash = level_hash[x]
|
388
|
+
end
|
389
|
+
if level_hash.is_a? Hash # When there's only one
|
390
|
+
ret << level_hash
|
391
|
+
else # should be array
|
392
|
+
# puts 'level_hash=' + level_hash.inspect
|
393
|
+
level_hash.each do |x|
|
394
|
+
ret << x
|
395
|
+
end
|
396
|
+
end
|
397
|
+
elsif params[:pull_out_single]
|
398
|
+
# returns a single object
|
399
|
+
ret = AwsResponseObjectHash.new(parsed[:response_metadata])
|
400
|
+
level_hash = parsed
|
401
|
+
params[:pull_out_single].each do |x|
|
402
|
+
level_hash = level_hash[x]
|
403
|
+
end
|
404
|
+
ret.merge!(level_hash)
|
405
|
+
else
|
406
|
+
ret = parsed
|
407
|
+
end
|
408
|
+
return ret
|
409
|
+
|
410
|
+
else
|
411
|
+
@error_handler = AWSErrorHandler.new(self, nil, :errors_list => self.class.amazon_problems) unless @error_handler
|
412
|
+
check_result = @error_handler.check(request)
|
413
|
+
if check_result
|
414
|
+
@error_handler = nil
|
415
|
+
return check_result
|
416
|
+
end
|
417
|
+
request_text_data = "#{request[:server]}:#{request[:port]}#{request[:request].path}"
|
418
|
+
raise AwsError2.new(@last_response.code, @last_request_id, request_text_data, @last_response.body)
|
419
|
+
end
|
420
|
+
ensure
|
421
|
+
connection.finish if connection && lib_params[:connection_mode] == :per_request
|
422
|
+
end
|
423
|
+
|
424
|
+
end
|
425
|
+
|
426
|
+
# This is the latest and greatest now. Service must have connection_name defined.
|
427
|
+
def request_info_xml_simple3(service_interface, request, options)
|
428
|
+
request_info_xml_simple(service_interface.class.connection_name,
|
429
|
+
service_interface.params,
|
430
|
+
request,
|
431
|
+
service_interface.logger,
|
432
|
+
options)
|
433
|
+
end
|
434
|
+
|
435
|
+
def symbolize(hash, force_array)
|
436
|
+
ret = {}
|
437
|
+
hash.keys.each do |key|
|
438
|
+
val = hash[key]
|
439
|
+
if val.is_a? Hash
|
440
|
+
val = symbolize(val, force_array)
|
441
|
+
if force_array.include? key
|
442
|
+
val = [val]
|
443
|
+
end
|
444
|
+
elsif val.is_a? Array
|
445
|
+
val = val.collect { |x| symbolize(x, force_array) }
|
446
|
+
end
|
447
|
+
ret[Aws::Utils.underscore(key).to_sym] = val
|
448
|
+
end
|
449
|
+
ret
|
450
|
+
end
|
451
|
+
|
452
|
+
# Returns +true+ if the describe_xxx responses are being cached
|
453
|
+
def caching?
|
454
|
+
@params.key?(:cache) ? @params[:cache] : @@caching
|
455
|
+
end
|
456
|
+
|
457
|
+
# Check if the aws function response hits the cache or not.
|
458
|
+
# If the cache hits:
|
459
|
+
# - raises an +AwsNoChange+ exception if +do_raise+ == +:raise+.
|
460
|
+
# - returnes parsed response from the cache if it exists or +true+ otherwise.
|
461
|
+
# If the cache miss or the caching is off then returns +false+.
|
462
|
+
def cache_hits?(function, response, do_raise=:raise)
|
463
|
+
result = false
|
464
|
+
if caching?
|
465
|
+
function = function.to_sym
|
466
|
+
# get rid of requestId (this bad boy was added for API 2008-08-08+ and it is uniq for every response)
|
467
|
+
response = response.sub(%r{<requestId>.+?</requestId>}, '')
|
468
|
+
response_md5 =Digest::MD5.hexdigest(response).to_s
|
469
|
+
# check for changes
|
470
|
+
unless @cache[function] && @cache[function][:response_md5] == response_md5
|
471
|
+
# well, the response is new, reset cache data
|
472
|
+
update_cache(function, {:response_md5 => response_md5,
|
473
|
+
:timestamp => Time.now,
|
474
|
+
:hits => 0,
|
475
|
+
:parsed => nil})
|
476
|
+
else
|
477
|
+
# aha, cache hits, update the data and throw an exception if needed
|
478
|
+
@cache[function][:hits] += 1
|
479
|
+
if do_raise == :raise
|
480
|
+
raise(AwsNoChange, "Cache hit: #{function} response has not changed since "+
|
481
|
+
"#{@cache[function][:timestamp].strftime('%Y-%m-%d %H:%M:%S')}, "+
|
482
|
+
"hits: #{@cache[function][:hits]}.")
|
483
|
+
else
|
484
|
+
result = @cache[function][:parsed] || true
|
485
|
+
end
|
486
|
+
end
|
487
|
+
end
|
488
|
+
result
|
489
|
+
end
|
490
|
+
|
491
|
+
def update_cache(function, hash)
|
492
|
+
(@cache[function.to_sym] ||= {}).merge!(hash) if caching?
|
493
|
+
end
|
494
|
+
|
495
|
+
def on_exception(options={:raise=>true, :log=>true}) # :nodoc:
|
496
|
+
raise if $!.is_a?(AwsNoChange)
|
497
|
+
AwsError::on_aws_exception(self, options)
|
498
|
+
end
|
499
|
+
|
500
|
+
# Return +true+ if this instance works in multi_thread mode and +false+ otherwise.
|
501
|
+
def multi_thread
|
502
|
+
@params[:multi_thread]
|
503
|
+
end
|
504
|
+
|
505
|
+
|
506
|
+
def request_info_impl(connection, benchblock, request, parser, options={}, &block) #:nodoc:
|
507
|
+
connection = connection
|
508
|
+
@last_request = request[:request]
|
509
|
+
@last_response = nil
|
510
|
+
response =nil
|
511
|
+
blockexception = nil
|
512
|
+
|
513
|
+
# puts 'OPTIONS2=' + options.inspect
|
514
|
+
|
515
|
+
if (block != nil)
|
516
|
+
# TRB 9/17/07 Careful - because we are passing in blocks, we get a situation where
|
517
|
+
# an exception may get thrown in the block body (which is high-level
|
518
|
+
# code either here or in the application) but gets caught in the
|
519
|
+
# low-level code of HttpConnection. The solution is not to let any
|
520
|
+
# exception escape the block that we pass to HttpConnection::request.
|
521
|
+
# Exceptions can originate from code directly in the block, or from user
|
522
|
+
# code called in the other block which is passed to response.read_body.
|
523
|
+
benchblock.service.add! do
|
524
|
+
responsehdr = connection.request(request) do |response|
|
525
|
+
#########
|
526
|
+
begin
|
527
|
+
@last_response = response
|
528
|
+
if response.is_a?(Net::HTTPSuccess)
|
529
|
+
@error_handler = nil
|
530
|
+
response.read_body(&block)
|
531
|
+
else
|
532
|
+
@error_handler = AWSErrorHandler.new(self, parser, :errors_list => self.class.amazon_problems) unless @error_handler
|
533
|
+
check_result = @error_handler.check(request, options)
|
534
|
+
if check_result
|
535
|
+
@error_handler = nil
|
536
|
+
return check_result
|
537
|
+
end
|
538
|
+
request_text_data = "#{request[:server]}:#{request[:port]}#{request[:request].path}"
|
539
|
+
raise AwsError.new(@last_errors, @last_response.code, @last_request_id, request_text_data)
|
540
|
+
end
|
541
|
+
rescue Exception => e
|
542
|
+
blockexception = e
|
543
|
+
end
|
544
|
+
end
|
545
|
+
#########
|
546
|
+
|
547
|
+
#OK, now we are out of the block passed to the lower level
|
548
|
+
if (blockexception)
|
549
|
+
raise blockexception
|
550
|
+
end
|
551
|
+
benchblock.xml.add! do
|
552
|
+
parser.parse(responsehdr)
|
553
|
+
end
|
554
|
+
return parser.result
|
555
|
+
end
|
556
|
+
else
|
557
|
+
benchblock.service.add! { response = connection.request(request) }
|
558
|
+
# check response for errors...
|
559
|
+
@last_response = response
|
560
|
+
if response.is_a?(Net::HTTPSuccess)
|
561
|
+
@error_handler = nil
|
562
|
+
benchblock.xml.add! { parser.parse(response) }
|
563
|
+
return parser.result
|
564
|
+
else
|
565
|
+
@error_handler = AWSErrorHandler.new(self, parser, :errors_list => self.class.amazon_problems) unless @error_handler
|
566
|
+
check_result = @error_handler.check(request, options)
|
567
|
+
if check_result
|
568
|
+
@error_handler = nil
|
569
|
+
return check_result
|
570
|
+
end
|
571
|
+
request_text_data = "#{request[:server]}:#{request[:port]}#{request[:request].path}"
|
572
|
+
raise AwsError.new(@last_errors, @last_response.code, @last_request_id, request_text_data)
|
573
|
+
end
|
574
|
+
end
|
575
|
+
rescue
|
576
|
+
@error_handler = nil
|
577
|
+
raise
|
578
|
+
end
|
579
|
+
|
580
|
+
def request_cache_or_info(method, link, parser_class, benchblock, use_cache=true) #:nodoc:
|
581
|
+
# We do not want to break the logic of parsing hence will use a dummy parser to process all the standard
|
582
|
+
# steps (errors checking etc). The dummy parser does nothig - just returns back the params it received.
|
583
|
+
# If the caching is enabled and hit then throw AwsNoChange.
|
584
|
+
# P.S. caching works for the whole images list only! (when the list param is blank)
|
585
|
+
# check cache
|
586
|
+
response, params = request_info(link, RightDummyParser.new)
|
587
|
+
cache_hits?(method.to_sym, response.body) if use_cache
|
588
|
+
parser = parser_class.new(:logger => @logger)
|
589
|
+
benchblock.xml.add! { parser.parse(response, params) }
|
590
|
+
result = block_given? ? yield(parser) : parser.result
|
591
|
+
# update parsed data
|
592
|
+
update_cache(method.to_sym, :parsed => result) if use_cache
|
593
|
+
result
|
594
|
+
end
|
595
|
+
|
596
|
+
# Returns Amazons request ID for the latest request
|
597
|
+
def last_request_id
|
598
|
+
@last_response && @last_response.body.to_s[%r{<requestId>(.+?)</requestId>}] && $1
|
599
|
+
end
|
600
|
+
|
601
|
+
def hash_params(prefix, list) #:nodoc:
|
602
|
+
groups = {}
|
603
|
+
list.each_index { |i| groups.update("#{prefix}.#{i+1}"=>list[i]) } if list
|
604
|
+
return groups
|
605
|
+
end
|
606
|
+
|
607
|
+
end
|
608
|
+
|
609
|
+
|
610
|
+
end
|
611
|
+
|
@@ -0,0 +1,39 @@
|
|
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
|
+
|
25
|
+
|
26
|
+
# A hack because there's a bug in add! in Benchmark::Tms
|
27
|
+
module Benchmark #:nodoc:
|
28
|
+
class Tms #:nodoc:
|
29
|
+
def add!(&blk)
|
30
|
+
t = Benchmark::measure(&blk)
|
31
|
+
@utime = utime + t.utime
|
32
|
+
@stime = stime + t.stime
|
33
|
+
@cutime = cutime + t.cutime
|
34
|
+
@cstime = cstime + t.cstime
|
35
|
+
@real = real + t.real
|
36
|
+
self
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|