kerryb-right_aws 1.7.6 → 1.10.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +78 -1
- data/Manifest.txt +3 -1
- data/README.txt +28 -8
- data/Rakefile +23 -7
- data/lib/acf/right_acf_interface.rb +379 -0
- data/lib/awsbase/right_awsbase.rb +150 -12
- data/lib/ec2/right_ec2.rb +595 -77
- data/lib/right_aws.rb +3 -3
- data/lib/s3/right_s3_interface.rb +3 -3
- data/lib/sdb/active_sdb.rb +267 -32
- data/lib/sdb/right_sdb_interface.rb +272 -48
- data/lib/sqs/right_sqs.rb +8 -0
- data/lib/sqs/right_sqs_gen2.rb +8 -0
- data/lib/sqs/right_sqs_gen2_interface.rb +17 -22
- data/lib/sqs/right_sqs_interface.rb +7 -13
- data/test/acf/test_helper.rb +2 -0
- data/test/acf/test_right_acf.rb +146 -0
- data/test/ec2/test_right_ec2.rb +32 -0
- data/test/s3/test_right_s3.rb +41 -10
- data/test/sdb/test_active_sdb.rb +59 -9
- data/test/sdb/test_helper.rb +1 -0
- data/test/sdb/test_right_sdb.rb +106 -6
- data/test/ts_right_aws.rb +1 -0
- metadata +30 -21
- data/lib/awsbase/file_fix.rb +0 -33
@@ -24,11 +24,73 @@
|
|
24
24
|
# Test
|
25
25
|
module RightAws
|
26
26
|
require 'md5'
|
27
|
+
require 'pp'
|
27
28
|
|
28
29
|
class AwsUtils #:nodoc:
|
29
|
-
@@
|
30
|
+
@@digest1 = OpenSSL::Digest::Digest.new("sha1")
|
31
|
+
@@digest256 = nil
|
32
|
+
if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000
|
33
|
+
@@digest256 = OpenSSL::Digest::Digest.new("sha256") rescue nil # Some installation may not support sha256
|
34
|
+
end
|
35
|
+
|
30
36
|
def self.sign(aws_secret_access_key, auth_string)
|
31
|
-
Base64.encode64(OpenSSL::HMAC.digest(@@
|
37
|
+
Base64.encode64(OpenSSL::HMAC.digest(@@digest1, aws_secret_access_key, auth_string)).strip
|
38
|
+
end
|
39
|
+
|
40
|
+
# Escape a string accordingly Amazon rulles
|
41
|
+
# http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?REST_RESTAuth.html
|
42
|
+
def self.amz_escape(param)
|
43
|
+
param.to_s.gsub(/([^a-zA-Z0-9._~-]+)/n) do
|
44
|
+
'%' + $1.unpack('H2' * $1.size).join('%').upcase
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Set a timestamp and a signature version
|
49
|
+
def self.fix_service_params(service_hash, signature)
|
50
|
+
# Removed hardcoded UTC timezone
|
51
|
+
service_hash["Timestamp"] ||= Time.now.strftime("%Y-%m-%dT%H:%M:%S.000Z") unless service_hash["Expires"]
|
52
|
+
service_hash["SignatureVersion"] = signature
|
53
|
+
service_hash
|
54
|
+
end
|
55
|
+
|
56
|
+
# Signature Version 0
|
57
|
+
# A deprecated guy (should work till septemper 2009)
|
58
|
+
def self.sign_request_v0(aws_secret_access_key, service_hash)
|
59
|
+
fix_service_params(service_hash, '0')
|
60
|
+
string_to_sign = "#{service_hash['Action']}#{service_hash['Timestamp'] || service_hash['Expires']}"
|
61
|
+
service_hash['Signature'] = AwsUtils::sign(aws_secret_access_key, string_to_sign)
|
62
|
+
service_hash.to_a.collect{|key,val| "#{amz_escape(key)}=#{amz_escape(val.to_s)}" }.join("&")
|
63
|
+
end
|
64
|
+
|
65
|
+
# Signature Version 1
|
66
|
+
# Another deprecated guy (should work till septemper 2009)
|
67
|
+
def self.sign_request_v1(aws_secret_access_key, service_hash)
|
68
|
+
fix_service_params(service_hash, '1')
|
69
|
+
string_to_sign = service_hash.sort{|a,b| (a[0].to_s.downcase)<=>(b[0].to_s.downcase)}.to_s
|
70
|
+
service_hash['Signature'] = AwsUtils::sign(aws_secret_access_key, string_to_sign)
|
71
|
+
service_hash.to_a.collect{|key,val| "#{amz_escape(key)}=#{amz_escape(val.to_s)}" }.join("&")
|
72
|
+
end
|
73
|
+
|
74
|
+
# Signature Version 2
|
75
|
+
# EC2, SQS and SDB requests must be signed by this guy.
|
76
|
+
# See: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?REST_RESTAuth.html
|
77
|
+
# http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1928
|
78
|
+
def self.sign_request_v2(aws_secret_access_key, service_hash, http_verb, host, uri)
|
79
|
+
fix_service_params(service_hash, '2')
|
80
|
+
# select a signing method (make an old openssl working with sha1)
|
81
|
+
# make 'HmacSHA256' to be a default one
|
82
|
+
service_hash['SignatureMethod'] = 'HmacSHA256' unless ['HmacSHA256', 'HmacSHA1'].include?(service_hash['SignatureMethod'])
|
83
|
+
service_hash['SignatureMethod'] = 'HmacSHA1' unless @@digest256
|
84
|
+
# select a digest
|
85
|
+
digest = (service_hash['SignatureMethod'] == 'HmacSHA256' ? @@digest256 : @@digest1)
|
86
|
+
# form string to sign
|
87
|
+
canonical_string = service_hash.keys.sort.map do |key|
|
88
|
+
"#{amz_escape(key)}=#{amz_escape(service_hash[key])}"
|
89
|
+
end.join('&')
|
90
|
+
string_to_sign = "#{http_verb.to_s.upcase}\n#{host.downcase}\n#{uri}\n#{canonical_string}"
|
91
|
+
# sign the string
|
92
|
+
signature = amz_escape(Base64.encode64(OpenSSL::HMAC.digest(digest, aws_secret_access_key, string_to_sign)).strip)
|
93
|
+
"#{canonical_string}&Signature=#{signature}"
|
32
94
|
end
|
33
95
|
|
34
96
|
# From Amazon's SQS Dev Guide, a brief description of how to escape:
|
@@ -44,6 +106,23 @@ module RightAws
|
|
44
106
|
e = URI.escape(raw)
|
45
107
|
e.gsub(/\+/, "%2b")
|
46
108
|
end
|
109
|
+
|
110
|
+
def self.allow_only(allowed_keys, params)
|
111
|
+
bogus_args = []
|
112
|
+
params.keys.each {|p| bogus_args.push(p) unless allowed_keys.include?(p) }
|
113
|
+
raise AwsError.new("The following arguments were given but are not legal for the function call #{caller_method}: #{bogus_args.inspect}") if bogus_args.length > 0
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.mandatory_arguments(required_args, params)
|
117
|
+
rargs = required_args.dup
|
118
|
+
params.keys.each {|p| rargs.delete(p)}
|
119
|
+
raise AwsError.new("The following mandatory arguments were not provided to #{caller_method}: #{rargs.inspect}") if rargs.length > 0
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.caller_method
|
123
|
+
caller[1]=~/`(.*?)'/
|
124
|
+
$1
|
125
|
+
end
|
47
126
|
|
48
127
|
end
|
49
128
|
|
@@ -93,7 +172,7 @@ module RightAws
|
|
93
172
|
end
|
94
173
|
|
95
174
|
module RightAwsBaseInterface
|
96
|
-
DEFAULT_SIGNATURE_VERSION = '
|
175
|
+
DEFAULT_SIGNATURE_VERSION = '2'
|
97
176
|
|
98
177
|
@@caching = false
|
99
178
|
def self.caching
|
@@ -102,7 +181,7 @@ module RightAws
|
|
102
181
|
def self.caching=(caching)
|
103
182
|
@@caching = caching
|
104
183
|
end
|
105
|
-
|
184
|
+
|
106
185
|
# Current aws_access_key_id
|
107
186
|
attr_reader :aws_access_key_id
|
108
187
|
# Last HTTP request object
|
@@ -130,10 +209,20 @@ module RightAws
|
|
130
209
|
if aws_access_key_id.blank? || aws_secret_access_key.blank?
|
131
210
|
@aws_access_key_id = aws_access_key_id
|
132
211
|
@aws_secret_access_key = aws_secret_access_key
|
133
|
-
|
134
|
-
@params[:
|
135
|
-
|
136
|
-
|
212
|
+
# if the endpoint was explicitly defined - then use it
|
213
|
+
if @params[:endpoint_url]
|
214
|
+
@params[:server] = URI.parse(@params[:endpoint_url]).host
|
215
|
+
@params[:port] = URI.parse(@params[:endpoint_url]).port
|
216
|
+
@params[:service] = URI.parse(@params[:endpoint_url]).path
|
217
|
+
@params[:protocol] = URI.parse(@params[:endpoint_url]).scheme
|
218
|
+
@params[:region] = nil
|
219
|
+
else
|
220
|
+
@params[:server] ||= service_info[:default_host]
|
221
|
+
@params[:server] = "#{@params[:region]}.#{@params[:server]}" if @params[:region]
|
222
|
+
@params[:port] ||= service_info[:default_port]
|
223
|
+
@params[:service] ||= service_info[:default_service]
|
224
|
+
@params[:protocol] ||= service_info[:default_protocol]
|
225
|
+
end
|
137
226
|
@params[:multi_thread] ||= defined?(AWS_DAEMON)
|
138
227
|
@logger = @params[:logger]
|
139
228
|
@logger = RAILS_DEFAULT_LOGGER if !@logger && defined?(RAILS_DEFAULT_LOGGER)
|
@@ -144,6 +233,15 @@ module RightAws
|
|
144
233
|
@signature_version = (params[:signature_version] || DEFAULT_SIGNATURE_VERSION).to_s
|
145
234
|
end
|
146
235
|
|
236
|
+
def signed_service_params(aws_secret_access_key, service_hash, http_verb=nil, host=nil, service=nil )
|
237
|
+
case signature_version.to_s
|
238
|
+
when '0' then AwsUtils::sign_request_v0(aws_secret_access_key, service_hash)
|
239
|
+
when '1' then AwsUtils::sign_request_v1(aws_secret_access_key, service_hash)
|
240
|
+
when '2' then AwsUtils::sign_request_v2(aws_secret_access_key, service_hash, http_verb, host, service)
|
241
|
+
else raise AwsError.new("Unknown signature version (#{signature_version.to_s}) requested")
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
147
245
|
# Returns +true+ if the describe_xxx responses are being cached
|
148
246
|
def caching?
|
149
247
|
@params.key?(:cache) ? @params[:cache] : @@caching
|
@@ -157,10 +255,13 @@ module RightAws
|
|
157
255
|
def cache_hits?(function, response, do_raise=:raise)
|
158
256
|
result = false
|
159
257
|
if caching?
|
160
|
-
function
|
258
|
+
function = function.to_sym
|
259
|
+
# get rid of requestId (this bad boy was added for API 2008-08-08+ and it is uniq for every response)
|
260
|
+
response = response.sub(%r{<requestId>.+?</requestId>}, '')
|
161
261
|
response_md5 = MD5.md5(response).to_s
|
162
|
-
#
|
262
|
+
# check for changes
|
163
263
|
unless @cache[function] && @cache[function][:response_md5] == response_md5
|
264
|
+
# well, the response is new, reset cache data
|
164
265
|
update_cache(function, {:response_md5 => response_md5,
|
165
266
|
:timestamp => Time.now,
|
166
267
|
:hits => 0,
|
@@ -264,6 +365,27 @@ module RightAws
|
|
264
365
|
raise
|
265
366
|
end
|
266
367
|
|
368
|
+
def request_cache_or_info(method, link, parser_class, benchblock, use_cache=true) #:nodoc:
|
369
|
+
# We do not want to break the logic of parsing hence will use a dummy parser to process all the standard
|
370
|
+
# steps (errors checking etc). The dummy parser does nothig - just returns back the params it received.
|
371
|
+
# If the caching is enabled and hit then throw AwsNoChange.
|
372
|
+
# P.S. caching works for the whole images list only! (when the list param is blank)
|
373
|
+
# check cache
|
374
|
+
response, params = request_info(link, RightDummyParser.new)
|
375
|
+
cache_hits?(method.to_sym, response.body) if use_cache
|
376
|
+
parser = parser_class.new(:logger => @logger)
|
377
|
+
benchblock.xml.add!{ parser.parse(response, params) }
|
378
|
+
result = block_given? ? yield(parser) : parser.result
|
379
|
+
# update parsed data
|
380
|
+
update_cache(method.to_sym, :parsed => result) if use_cache
|
381
|
+
result
|
382
|
+
end
|
383
|
+
|
384
|
+
# Returns Amazons request ID for the latest request
|
385
|
+
def last_request_id
|
386
|
+
@last_response && @last_response.body.to_s[%r{<requestId>(.+?)</requestId>}] && $1
|
387
|
+
end
|
388
|
+
|
267
389
|
end
|
268
390
|
|
269
391
|
|
@@ -550,8 +672,9 @@ module RightAws
|
|
550
672
|
@xmlpath += @xmlpath.empty? ? name : "/#{name}"
|
551
673
|
end
|
552
674
|
def tag_end(name)
|
553
|
-
@xmlpath
|
554
|
-
|
675
|
+
if @xmlpath =~ /^(.*?)\/?#{name}$/
|
676
|
+
@xmlpath = $1
|
677
|
+
end
|
555
678
|
tagend(name)
|
556
679
|
end
|
557
680
|
def text(text)
|
@@ -656,5 +779,20 @@ module RightAws
|
|
656
779
|
end
|
657
780
|
end
|
658
781
|
|
782
|
+
# Dummy parser - does nothing
|
783
|
+
# Returns the original params back
|
784
|
+
class RightDummyParser # :nodoc:
|
785
|
+
attr_accessor :result
|
786
|
+
def parse(response, params={})
|
787
|
+
@result = [response, params]
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
class RightHttp2xxParser < RightAWSParser # :nodoc:
|
792
|
+
def parse(response)
|
793
|
+
@result = response.is_a?(Net::HTTPSuccess)
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
659
797
|
end
|
660
798
|
|