right_aws 1.9.0 → 1.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +25 -0
- data/README.txt +5 -3
- data/Rakefile +15 -6
- data/lib/acf/right_acf_interface.rb +1 -1
- data/lib/awsbase/right_awsbase.rb +89 -9
- data/lib/ec2/right_ec2.rb +105 -21
- data/lib/right_aws.rb +1 -1
- data/lib/s3/right_s3_interface.rb +32 -42
- data/lib/sdb/active_sdb.rb +178 -21
- data/lib/sdb/right_sdb_interface.rb +222 -27
- data/lib/sqs/right_sqs_gen2_interface.rb +14 -20
- data/lib/sqs/right_sqs_interface.rb +4 -11
- data/test/acf/test_right_acf.rb +1 -1
- data/test/ec2/test_right_ec2.rb +32 -0
- data/test/s3/test_right_s3.rb +2 -2
- data/test/sdb/test_active_sdb.rb +50 -7
- data/test/sdb/test_right_sdb.rb +59 -10
- metadata +6 -6
data/History.txt
CHANGED
@@ -219,3 +219,28 @@ the source key.
|
|
219
219
|
S3Interface::retrieve_object_and_verify (thanks to numerous user reports)
|
220
220
|
- Updates to caching for Ec2::describe_images_by methods
|
221
221
|
- Ec2 now has Ec2::last_request_id
|
222
|
+
|
223
|
+
=== 1.10.0
|
224
|
+
|
225
|
+
Release Notes:
|
226
|
+
|
227
|
+
The big new features are SDB's SQL-like query and query_with_attributes
|
228
|
+
support as well as signature v2 support for all services. There are also
|
229
|
+
numerous bug fixes, many of them reported and patched by users and
|
230
|
+
customers.
|
231
|
+
|
232
|
+
- AwsBase: signature v2 support added
|
233
|
+
- AwsBase: fix the regex matching for xmlpath which didn't work in Ruby
|
234
|
+
1.8.7 (thanks to a customer report)
|
235
|
+
- Ec2: describe_availability_zones improved to support regions
|
236
|
+
- Ec2: Disabled retries when EC2 returns "ServiceUnavailable: Request limit
|
237
|
+
exceeded"
|
238
|
+
- Ec2: Use POST for large queries; this avoids truncation of large user data
|
239
|
+
when launching instances (thanks to Bob for the report)
|
240
|
+
- CloudFront: docs fixes
|
241
|
+
- SDB: added: SQL-like query, select and query_with_attributes support
|
242
|
+
- SDB: fixed no method error when searching for id that doesn't exist
|
243
|
+
(thanks to multiple users for reporting this)
|
244
|
+
- S3: Fixed overzealous URL-encoding when generating URLs for S3 keys
|
245
|
+
(thanks to a bug report on the RubyForge forum)
|
246
|
+
|
data/README.txt
CHANGED
@@ -38,7 +38,7 @@ The RightScale AWS gems comprise:
|
|
38
38
|
AWS accounts.
|
39
39
|
- Support for both first- and second-generation SQS (API versions 2007-05-01
|
40
40
|
and 2008-01-01). These versions of SQS are not compatible.
|
41
|
-
- Support for signature versions 0 and
|
41
|
+
- Support for signature versions 0, 1 and 2 on all services.
|
42
42
|
- Interoperability with any cloud running Eucalyptus (http://eucalyptus.cs.ucsb.edu)
|
43
43
|
- Test suite (requires AWS account to do "live" testing).
|
44
44
|
|
@@ -119,7 +119,9 @@ multithreaded mode.
|
|
119
119
|
'incompatible Net::HTTP monkey-patch'
|
120
120
|
|
121
121
|
This is due to a conflict between the right_http_connection gem and another
|
122
|
-
gem required by attachment_fu.
|
122
|
+
gem required by attachment_fu. It may be possible to require right_aws (and
|
123
|
+
thus right_http_connection) in the .after_initialize method of the config object in
|
124
|
+
environment.rb (check the docs for Rails::Configuration.after_initialize).
|
123
125
|
|
124
126
|
- 8/07: Amazon has changed the semantics of the SQS service. A
|
125
127
|
new queue may not be created within 60 seconds of the destruction of any
|
@@ -142,7 +144,7 @@ sudo gem install right_aws
|
|
142
144
|
|
143
145
|
== LICENSE:
|
144
146
|
|
145
|
-
Copyright (c) 2007-
|
147
|
+
Copyright (c) 2007-2009 RightScale, Inc.
|
146
148
|
|
147
149
|
Permission is hereby granted, free of charge, to any person obtaining
|
148
150
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
@@ -10,27 +10,36 @@ require 'lib/right_aws.rb'
|
|
10
10
|
testglobs = ["test/ts_right_aws.rb"]
|
11
11
|
|
12
12
|
|
13
|
-
# Suppress Hoe's self-inclusion as a dependency for our Gem.
|
14
|
-
# Rake & rubyforge out of the dependency list.
|
13
|
+
# Suppress Hoe's self-inclusion as a dependency for our Gem. This also keeps
|
14
|
+
# Rake & rubyforge out of the dependency list. Users must manually install
|
15
15
|
# these gems to run tests, etc.
|
16
|
+
# TRB 2/24/09: also do this for the extra_dev_deps array present in newer hoes.
|
17
|
+
# Older versions of RubyGems will try to install developer-dependencies as
|
18
|
+
# required runtime dependencies. It would be great to take advantage of the extra dev deps, but not
|
19
|
+
# at the cost of requiring many additional dependencies if the user is running an older RubyGems.
|
16
20
|
class Hoe
|
17
21
|
def extra_deps
|
18
22
|
@extra_deps.reject do |x|
|
19
23
|
Array(x).first == 'hoe'
|
20
24
|
end
|
21
25
|
end
|
26
|
+
def extra_dev_deps
|
27
|
+
@extra_dev_deps.reject do |x|
|
28
|
+
Array(x).first == 'hoe'
|
29
|
+
end
|
30
|
+
end
|
22
31
|
end
|
23
32
|
|
24
33
|
Hoe.new('right_aws', RightAws::VERSION::STRING) do |p|
|
25
|
-
p.rubyforge_name = '
|
34
|
+
p.rubyforge_name = 'rightscale'
|
26
35
|
p.author = 'RightScale, Inc.'
|
27
|
-
p.email = '
|
28
|
-
p.summary = 'Interface classes for the Amazon EC2, SQS, and
|
36
|
+
p.email = 'rubygems@rightscale.com'
|
37
|
+
p.summary = 'Interface classes for the Amazon EC2/EBS, SQS, S3, SDB, and ACF Web Services'
|
29
38
|
p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
|
30
39
|
p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
|
31
40
|
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
32
41
|
p.remote_rdoc_dir = "/right_aws_gem_doc"
|
33
|
-
p.extra_deps = [['right_http_connection','>= 1.2.
|
42
|
+
p.extra_deps = [['right_http_connection','>= 1.2.4']]
|
34
43
|
p.test_globs = testglobs
|
35
44
|
end
|
36
45
|
|
@@ -198,7 +198,7 @@ module RightAws
|
|
198
198
|
# Create a new distribution.
|
199
199
|
# Returns the just created distribution or RightAws::AwsError exception.
|
200
200
|
#
|
201
|
-
# acf.create_distribution('bucket-for-k-dzreyev.s3.amazonaws.com', 'Woo-Hoo!', ['web1.my-awesome-site.net'] ) #=>
|
201
|
+
# acf.create_distribution('bucket-for-k-dzreyev.s3.amazonaws.com', 'Woo-Hoo!', true, ['web1.my-awesome-site.net'] ) #=>
|
202
202
|
# {:comment => "Woo-Hoo!",
|
203
203
|
# :enabled => true,
|
204
204
|
# :location => "https://cloudfront.amazonaws.com/2008-06-30/distribution/E2REJM3VUN5RSI",
|
@@ -27,9 +27,69 @@ module RightAws
|
|
27
27
|
require 'pp'
|
28
28
|
|
29
29
|
class AwsUtils #:nodoc:
|
30
|
-
@@
|
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
|
+
|
31
36
|
def self.sign(aws_secret_access_key, auth_string)
|
32
|
-
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
|
+
service_hash["Timestamp"] ||= Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.000Z") unless service_hash["Expires"]
|
51
|
+
service_hash["SignatureVersion"] = signature
|
52
|
+
service_hash
|
53
|
+
end
|
54
|
+
|
55
|
+
# Signature Version 0
|
56
|
+
# A deprecated guy (should work till septemper 2009)
|
57
|
+
def self.sign_request_v0(aws_secret_access_key, service_hash)
|
58
|
+
fix_service_params(service_hash, '0')
|
59
|
+
string_to_sign = "#{service_hash['Action']}#{service_hash['Timestamp'] || service_hash['Expires']}"
|
60
|
+
service_hash['Signature'] = AwsUtils::sign(aws_secret_access_key, string_to_sign)
|
61
|
+
service_hash.to_a.collect{|key,val| "#{amz_escape(key)}=#{amz_escape(val.to_s)}" }.join("&")
|
62
|
+
end
|
63
|
+
|
64
|
+
# Signature Version 1
|
65
|
+
# Another deprecated guy (should work till septemper 2009)
|
66
|
+
def self.sign_request_v1(aws_secret_access_key, service_hash)
|
67
|
+
fix_service_params(service_hash, '1')
|
68
|
+
string_to_sign = service_hash.sort{|a,b| (a[0].to_s.downcase)<=>(b[0].to_s.downcase)}.to_s
|
69
|
+
service_hash['Signature'] = AwsUtils::sign(aws_secret_access_key, string_to_sign)
|
70
|
+
service_hash.to_a.collect{|key,val| "#{amz_escape(key)}=#{amz_escape(val.to_s)}" }.join("&")
|
71
|
+
end
|
72
|
+
|
73
|
+
# Signature Version 2
|
74
|
+
# EC2, SQS and SDB requests must be signed by this guy.
|
75
|
+
# See: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?REST_RESTAuth.html
|
76
|
+
# http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1928
|
77
|
+
def self.sign_request_v2(aws_secret_access_key, service_hash, http_verb, host, uri)
|
78
|
+
fix_service_params(service_hash, '2')
|
79
|
+
# select a signing method (make an old openssl working with sha1)
|
80
|
+
# make 'HmacSHA256' to be a default one
|
81
|
+
service_hash['SignatureMethod'] = 'HmacSHA256' unless ['HmacSHA256', 'HmacSHA1'].include?(service_hash['SignatureMethod'])
|
82
|
+
service_hash['SignatureMethod'] = 'HmacSHA1' unless @@digest256
|
83
|
+
# select a digest
|
84
|
+
digest = (service_hash['SignatureMethod'] == 'HmacSHA256' ? @@digest256 : @@digest1)
|
85
|
+
# form string to sign
|
86
|
+
canonical_string = service_hash.keys.sort.map do |key|
|
87
|
+
"#{amz_escape(key)}=#{amz_escape(service_hash[key])}"
|
88
|
+
end.join('&')
|
89
|
+
string_to_sign = "#{http_verb.to_s.upcase}\n#{host.downcase}\n#{uri}\n#{canonical_string}"
|
90
|
+
# sign the string
|
91
|
+
signature = amz_escape(Base64.encode64(OpenSSL::HMAC.digest(digest, aws_secret_access_key, string_to_sign)).strip)
|
92
|
+
"#{canonical_string}&Signature=#{signature}"
|
33
93
|
end
|
34
94
|
|
35
95
|
# From Amazon's SQS Dev Guide, a brief description of how to escape:
|
@@ -111,7 +171,7 @@ module RightAws
|
|
111
171
|
end
|
112
172
|
|
113
173
|
module RightAwsBaseInterface
|
114
|
-
DEFAULT_SIGNATURE_VERSION = '
|
174
|
+
DEFAULT_SIGNATURE_VERSION = '2'
|
115
175
|
|
116
176
|
@@caching = false
|
117
177
|
def self.caching
|
@@ -148,10 +208,20 @@ module RightAws
|
|
148
208
|
if aws_access_key_id.blank? || aws_secret_access_key.blank?
|
149
209
|
@aws_access_key_id = aws_access_key_id
|
150
210
|
@aws_secret_access_key = aws_secret_access_key
|
151
|
-
|
152
|
-
@params[:
|
153
|
-
|
154
|
-
|
211
|
+
# if the endpoint was explicitly defined - then use it
|
212
|
+
if @params[:endpoint_url]
|
213
|
+
@params[:server] = URI.parse(@params[:endpoint_url]).host
|
214
|
+
@params[:port] = URI.parse(@params[:endpoint_url]).port
|
215
|
+
@params[:service] = URI.parse(@params[:endpoint_url]).path
|
216
|
+
@params[:protocol] = URI.parse(@params[:endpoint_url]).scheme
|
217
|
+
@params[:region] = nil
|
218
|
+
else
|
219
|
+
@params[:server] ||= service_info[:default_host]
|
220
|
+
@params[:server] = "#{@params[:region]}.#{@params[:server]}" if @params[:region]
|
221
|
+
@params[:port] ||= service_info[:default_port]
|
222
|
+
@params[:service] ||= service_info[:default_service]
|
223
|
+
@params[:protocol] ||= service_info[:default_protocol]
|
224
|
+
end
|
155
225
|
@params[:multi_thread] ||= defined?(AWS_DAEMON)
|
156
226
|
@logger = @params[:logger]
|
157
227
|
@logger = RAILS_DEFAULT_LOGGER if !@logger && defined?(RAILS_DEFAULT_LOGGER)
|
@@ -162,6 +232,15 @@ module RightAws
|
|
162
232
|
@signature_version = (params[:signature_version] || DEFAULT_SIGNATURE_VERSION).to_s
|
163
233
|
end
|
164
234
|
|
235
|
+
def signed_service_params(aws_secret_access_key, service_hash, http_verb=nil, host=nil, service=nil )
|
236
|
+
case signature_version.to_s
|
237
|
+
when '0' then AwsUtils::sign_request_v0(aws_secret_access_key, service_hash)
|
238
|
+
when '1' then AwsUtils::sign_request_v1(aws_secret_access_key, service_hash)
|
239
|
+
when '2' then AwsUtils::sign_request_v2(aws_secret_access_key, service_hash, http_verb, host, service)
|
240
|
+
else raise AwsError.new("Unknown signature version (#{signature_version.to_s}) requested")
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
165
244
|
# Returns +true+ if the describe_xxx responses are being cached
|
166
245
|
def caching?
|
167
246
|
@params.key?(:cache) ? @params[:cache] : @@caching
|
@@ -592,8 +671,9 @@ module RightAws
|
|
592
671
|
@xmlpath += @xmlpath.empty? ? name : "/#{name}"
|
593
672
|
end
|
594
673
|
def tag_end(name)
|
595
|
-
@xmlpath
|
596
|
-
|
674
|
+
if @xmlpath =~ /^(.*?)\/?#{name}$/
|
675
|
+
@xmlpath = $1
|
676
|
+
end
|
597
677
|
tagend(name)
|
598
678
|
end
|
599
679
|
def text(text)
|
data/lib/ec2/right_ec2.rb
CHANGED
@@ -68,7 +68,7 @@ module RightAws
|
|
68
68
|
include RightAwsBaseInterface
|
69
69
|
|
70
70
|
# Amazon EC2 API version being used
|
71
|
-
API_VERSION = "2008-
|
71
|
+
API_VERSION = "2008-12-01"
|
72
72
|
DEFAULT_HOST = "ec2.amazonaws.com"
|
73
73
|
DEFAULT_PATH = '/'
|
74
74
|
DEFAULT_PROTOCOL = 'https'
|
@@ -100,7 +100,9 @@ module RightAws
|
|
100
100
|
# Create a new handle to an EC2 account. All handles share the same per process or per thread
|
101
101
|
# HTTP connection to Amazon EC2. Each handle is for a specific account. The params have the
|
102
102
|
# following options:
|
103
|
+
# * <tt>:endpoint_url</tt> a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol and :region). Example: 'https://eu-west-1.ec2.amazonaws.com/'
|
103
104
|
# * <tt>:server</tt>: EC2 service host, default: DEFAULT_HOST
|
105
|
+
# * <tt>:region</tt>: EC2 region (North America by default)
|
104
106
|
# * <tt>:port</tt>: EC2 service port, default: DEFAULT_PORT
|
105
107
|
# * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL
|
106
108
|
# * <tt>:multi_thread</tt>: true=HTTP connection per thread, false=per process
|
@@ -120,24 +122,34 @@ module RightAws
|
|
120
122
|
aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'] ,
|
121
123
|
aws_secret_access_key|| ENV['AWS_SECRET_ACCESS_KEY'],
|
122
124
|
params)
|
125
|
+
# EC2 doesn't really define any transient errors to retry, and in fact,
|
126
|
+
# when they return a 503 it is usually for 'request limit exceeded' which
|
127
|
+
# we most certainly should not retry. So let's pare down the list of
|
128
|
+
# retryable errors to InternalError only (see RightAwsBase for the default
|
129
|
+
# list)
|
130
|
+
amazon_problems = ['InternalError']
|
123
131
|
end
|
124
132
|
|
125
133
|
|
126
134
|
def generate_request(action, params={}) #:nodoc:
|
127
|
-
service_hash = {"Action"
|
128
|
-
"AWSAccessKeyId"
|
129
|
-
"Version"
|
130
|
-
"Timestamp" => Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.000Z"),
|
131
|
-
"SignatureVersion" => signature_version }
|
135
|
+
service_hash = {"Action" => action,
|
136
|
+
"AWSAccessKeyId" => @aws_access_key_id,
|
137
|
+
"Version" => @@api }
|
132
138
|
service_hash.update(params)
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
139
|
+
service_params = signed_service_params(@aws_secret_access_key, service_hash, :get, @params[:server], @params[:service])
|
140
|
+
|
141
|
+
# use POST method if the length of the query string is too large
|
142
|
+
if service_params.size > 2000
|
143
|
+
if signature_version == '2'
|
144
|
+
# resign the request because HTTP verb is included into signature
|
145
|
+
service_params = signed_service_params(@aws_secret_access_key, service_hash, :post, @params[:server], @params[:service])
|
146
|
+
end
|
147
|
+
request = Net::HTTP::Post.new(service)
|
148
|
+
request.body = service_params
|
149
|
+
request['Content-Type'] = 'application/x-www-form-urlencoded'
|
150
|
+
else
|
151
|
+
request = Net::HTTP::Get.new("#{@params[:service]}?#{service_params}")
|
152
|
+
end
|
141
153
|
# prepare output hash
|
142
154
|
{ :request => request,
|
143
155
|
:server => @params[:server],
|
@@ -521,7 +533,7 @@ module RightAws
|
|
521
533
|
# Amazon 169.254.169.254 does not like escaped symbols!
|
522
534
|
# And it doesn't like "\n" inside of encoded string! Grrr....
|
523
535
|
# Otherwise, some of UserData symbols will be lost...
|
524
|
-
params['UserData'] = Base64.encode64(lparams[:user_data]).delete("\n") unless lparams[:user_data].blank?
|
536
|
+
params['UserData'] = Base64.encode64(lparams[:user_data]).delete("\n").strip unless lparams[:user_data].blank?
|
525
537
|
end
|
526
538
|
link = generate_request("RunInstances", params)
|
527
539
|
#debugger
|
@@ -969,11 +981,13 @@ module RightAws
|
|
969
981
|
# Describes availability zones that are currently available to the account and their states.
|
970
982
|
# Returns an array of 2 keys (:zone_name and :zone_state) hashes:
|
971
983
|
#
|
972
|
-
# ec2.describe_availability_zones #=> [{:
|
973
|
-
#
|
974
|
-
#
|
984
|
+
# ec2.describe_availability_zones #=> [{:region_name=>"us-east-1",
|
985
|
+
# :zone_name=>"us-east-1a",
|
986
|
+
# :zone_state=>"available"}, ... ]
|
975
987
|
#
|
976
|
-
# ec2.describe_availability_zones('us-east-1c') #=> [{:
|
988
|
+
# ec2.describe_availability_zones('us-east-1c') #=> [{:region_name=>"us-east-1",
|
989
|
+
# :zone_state=>"available",
|
990
|
+
# :zone_name=>"us-east-1c"}]
|
977
991
|
#
|
978
992
|
def describe_availability_zones(list=[])
|
979
993
|
link = generate_request("DescribeAvailabilityZones",
|
@@ -983,6 +997,23 @@ module RightAws
|
|
983
997
|
on_exception
|
984
998
|
end
|
985
999
|
|
1000
|
+
#-----------------------------------------------------------------
|
1001
|
+
# Regions
|
1002
|
+
#-----------------------------------------------------------------
|
1003
|
+
|
1004
|
+
# Describe regions.
|
1005
|
+
#
|
1006
|
+
# ec2.describe_regions #=> ["eu-west-1", "us-east-1"]
|
1007
|
+
#
|
1008
|
+
def describe_regions(list=[])
|
1009
|
+
link = generate_request("DescribeRegions",
|
1010
|
+
hash_params('RegionName',list.to_a))
|
1011
|
+
request_cache_or_info :describe_regions, link, QEc2DescribeRegionsParser, @@bench, list.blank?
|
1012
|
+
rescue Exception
|
1013
|
+
on_exception
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
|
986
1017
|
#-----------------------------------------------------------------
|
987
1018
|
# EBS: Volumes
|
988
1019
|
#-----------------------------------------------------------------
|
@@ -1133,6 +1164,45 @@ module RightAws
|
|
1133
1164
|
rescue Exception
|
1134
1165
|
on_exception
|
1135
1166
|
end
|
1167
|
+
|
1168
|
+
# Create a snapshot of specified volume, but with the normal retry algorithms disabled.
|
1169
|
+
# This method will return immediately upon error. The user can specify connect and read timeouts (in s)
|
1170
|
+
# for the connection to AWS. If the user does not specify timeouts, try_create_snapshot uses the default values
|
1171
|
+
# in Rightscale::HttpConnection.
|
1172
|
+
#
|
1173
|
+
# ec2.try_create_snapshot('vol-898a6fe0') #=>
|
1174
|
+
# {:aws_volume_id => "vol-fd9f7a94",
|
1175
|
+
# :aws_started_at => Tue Jun 24 18:40:40 UTC 2008,
|
1176
|
+
# :aws_progress => "",
|
1177
|
+
# :aws_status => "pending",
|
1178
|
+
# :aws_id => "snap-d56783bc"}
|
1179
|
+
#
|
1180
|
+
def try_create_snapshot(volume_id, connect_timeout = nil, read_timeout = nil)
|
1181
|
+
# For safety in the ensure block...we don't want to restore values
|
1182
|
+
# if we never read them in the first place
|
1183
|
+
orig_reiteration_time = nil
|
1184
|
+
orig_http_params = nil
|
1185
|
+
|
1186
|
+
orig_reiteration_time = RightAws::AWSErrorHandler::reiteration_time
|
1187
|
+
RightAws::AWSErrorHandler::reiteration_time = 0
|
1188
|
+
|
1189
|
+
orig_http_params = Rightscale::HttpConnection::params()
|
1190
|
+
new_http_params = orig_http_params.dup
|
1191
|
+
new_http_params[:http_connection_retry_count] = 0
|
1192
|
+
new_http_params[:http_connection_open_timeout] = connect_timeout if !connect_timeout.nil?
|
1193
|
+
new_http_params[:http_connection_read_timeout] = read_timeout if !read_timeout.nil?
|
1194
|
+
Rightscale::HttpConnection::params = new_http_params
|
1195
|
+
|
1196
|
+
link = generate_request("CreateSnapshot",
|
1197
|
+
"VolumeId" => volume_id.to_s)
|
1198
|
+
request_info(link, QEc2CreateSnapshotParser.new(:logger => @logger))
|
1199
|
+
|
1200
|
+
rescue Exception
|
1201
|
+
on_exception
|
1202
|
+
ensure
|
1203
|
+
RightAws::AWSErrorHandler::reiteration_time = orig_reiteration_time if orig_reiteration_time
|
1204
|
+
Rightscale::HttpConnection::params = orig_http_params if orig_http_params
|
1205
|
+
end
|
1136
1206
|
|
1137
1207
|
# Delete the specified snapshot.
|
1138
1208
|
#
|
@@ -1527,8 +1597,9 @@ module RightAws
|
|
1527
1597
|
end
|
1528
1598
|
def tagend(name)
|
1529
1599
|
case name
|
1530
|
-
when '
|
1531
|
-
when '
|
1600
|
+
when 'regionName' then @zone[:region_name] = @text
|
1601
|
+
when 'zoneName' then @zone[:zone_name] = @text
|
1602
|
+
when 'zoneState' then @zone[:zone_state] = @text
|
1532
1603
|
when 'item' then @result << @zone
|
1533
1604
|
end
|
1534
1605
|
end
|
@@ -1537,6 +1608,19 @@ module RightAws
|
|
1537
1608
|
end
|
1538
1609
|
end
|
1539
1610
|
|
1611
|
+
#-----------------------------------------------------------------
|
1612
|
+
# PARSERS: Regions
|
1613
|
+
#-----------------------------------------------------------------
|
1614
|
+
|
1615
|
+
class QEc2DescribeRegionsParser < RightAWSParser #:nodoc:
|
1616
|
+
def tagend(name)
|
1617
|
+
@result << @text if name == 'regionName'
|
1618
|
+
end
|
1619
|
+
def reset
|
1620
|
+
@result = []
|
1621
|
+
end
|
1622
|
+
end
|
1623
|
+
|
1540
1624
|
#-----------------------------------------------------------------
|
1541
1625
|
# PARSERS: EBS - Volumes
|
1542
1626
|
#-----------------------------------------------------------------
|
data/lib/right_aws.rb
CHANGED
@@ -32,6 +32,7 @@ module RightAws
|
|
32
32
|
DEFAULT_HOST = 's3.amazonaws.com'
|
33
33
|
DEFAULT_PORT = 443
|
34
34
|
DEFAULT_PROTOCOL = 'https'
|
35
|
+
DEFAULT_SERVICE = '/'
|
35
36
|
REQUEST_TTL = 30
|
36
37
|
DEFAULT_EXPIRES_AFTER = 1 * 24 * 60 * 60 # One day's worth of seconds
|
37
38
|
ONE_YEAR_IN_SECONDS = 365 * 24 * 60 * 60
|
@@ -62,7 +63,8 @@ module RightAws
|
|
62
63
|
def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})
|
63
64
|
init({ :name => 'S3',
|
64
65
|
:default_host => ENV['S3_URL'] ? URI.parse(ENV['S3_URL']).host : DEFAULT_HOST,
|
65
|
-
:default_port => ENV['S3_URL'] ? URI.parse(ENV['S3_URL']).port : DEFAULT_PORT,
|
66
|
+
:default_port => ENV['S3_URL'] ? URI.parse(ENV['S3_URL']).port : DEFAULT_PORT,
|
67
|
+
:default_service => ENV['S3_URL'] ? URI.parse(ENV['S3_URL']).path : DEFAULT_SERVICE,
|
66
68
|
:default_protocol => ENV['S3_URL'] ? URI.parse(ENV['S3_URL']).scheme : DEFAULT_PROTOCOL },
|
67
69
|
aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'],
|
68
70
|
aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'],
|
@@ -108,29 +110,34 @@ module RightAws
|
|
108
110
|
end
|
109
111
|
true
|
110
112
|
end
|
111
|
-
|
112
|
-
|
113
|
-
# Assumes that headers[:url] is URL encoded (use CGI::escape)
|
114
|
-
def generate_rest_request(method, headers) # :nodoc:
|
113
|
+
|
114
|
+
def fetch_request_params(headers) #:nodoc:
|
115
115
|
# default server to use
|
116
|
-
server
|
117
|
-
|
118
|
-
|
119
|
-
path_to_sign = "/#{path_to_sign}" unless path_to_sign[/^\//]
|
116
|
+
server = @params[:server]
|
117
|
+
service = @params[:service].to_s
|
118
|
+
service.chop! if service[%r{/$}] # remove trailing '/' from service
|
120
119
|
# extract bucket name and check it's dns compartibility
|
121
|
-
|
120
|
+
headers[:url].to_s[%r{^([a-z0-9._-]*)(/[^?]*)?(\?.+)?}i]
|
122
121
|
bucket_name, key_path, params_list = $1, $2, $3
|
123
122
|
# select request model
|
124
123
|
if is_dns_bucket?(bucket_name)
|
125
|
-
#
|
124
|
+
# fix a path
|
126
125
|
server = "#{bucket_name}.#{server}"
|
127
|
-
|
128
|
-
path = "#{key_path
|
129
|
-
# refactor the path (add '/' before params_list if the key is empty)
|
130
|
-
path_to_sign = "/#{bucket_name}#{path}"
|
126
|
+
key_path ||= '/'
|
127
|
+
path = "#{service}#{key_path}#{params_list}"
|
131
128
|
else
|
132
|
-
path =
|
129
|
+
path = "#{service}/#{bucket_name}#{key_path}#{params_list}"
|
133
130
|
end
|
131
|
+
path_to_sign = "#{service}/#{bucket_name}#{key_path}#{params_list}"
|
132
|
+
# path_to_sign = "/#{bucket_name}#{key_path}#{params_list}"
|
133
|
+
[ server, path, path_to_sign ]
|
134
|
+
end
|
135
|
+
|
136
|
+
# Generates request hash for REST API.
|
137
|
+
# Assumes that headers[:url] is URL encoded (use CGI::escape)
|
138
|
+
def generate_rest_request(method, headers) # :nodoc:
|
139
|
+
# calculate request data
|
140
|
+
server, path, path_to_sign = fetch_request_params(headers)
|
134
141
|
data = headers[:data]
|
135
142
|
# remove unset(==optional) and symbolyc keys
|
136
143
|
headers.each{ |key, value| headers.delete(key) if (value.nil? || key.is_a?(Symbol)) }
|
@@ -808,26 +815,9 @@ module RightAws
|
|
808
815
|
|
809
816
|
# Generates link for QUERY API
|
810
817
|
def generate_link(method, headers={}, expires=nil) #:nodoc:
|
811
|
-
|
812
|
-
server =
|
813
|
-
|
814
|
-
path_to_sign = headers[:url]
|
815
|
-
path_to_sign = "/#{path_to_sign}" unless path_to_sign[/^\//]
|
816
|
-
# extract bucket name and check it's dns compartibility
|
817
|
-
path_to_sign[%r{^/([a-z0-9._-]*)(/[^?]*)?(\?.+)?}i]
|
818
|
-
bucket_name, key_path, params_list = $1, $2, $3
|
819
|
-
# select request model
|
820
|
-
if is_dns_bucket?(bucket_name)
|
821
|
-
# add backet to a server name
|
822
|
-
server = "#{bucket_name}.#{server}"
|
823
|
-
# remove bucket from the path
|
824
|
-
path = "#{key_path || '/'}#{params_list}"
|
825
|
-
# refactor the path (add '/' before params_list if the key is empty)
|
826
|
-
path_to_sign = "/#{bucket_name}#{path}"
|
827
|
-
else
|
828
|
-
path = path_to_sign
|
829
|
-
end
|
830
|
-
# expiration time
|
818
|
+
# calculate request data
|
819
|
+
server, path, path_to_sign = fetch_request_params(headers)
|
820
|
+
# expiration time
|
831
821
|
expires ||= DEFAULT_EXPIRES_AFTER
|
832
822
|
expires = Time.now.utc + expires if expires.is_a?(Fixnum) && (expires < ONE_YEAR_IN_SECONDS)
|
833
823
|
expires = expires.to_i
|
@@ -890,7 +880,7 @@ module RightAws
|
|
890
880
|
# s3.put_link('my_awesome_bucket',key, object) #=> url string
|
891
881
|
#
|
892
882
|
def put_link(bucket, key, data=nil, expires=nil, headers={})
|
893
|
-
generate_link('PUT', headers.merge(:url=>"#{bucket}/#{
|
883
|
+
generate_link('PUT', headers.merge(:url=>"#{bucket}/#{AwsUtils::URLencode key}", :data=>data), expires)
|
894
884
|
rescue
|
895
885
|
on_exception
|
896
886
|
end
|
@@ -908,7 +898,7 @@ module RightAws
|
|
908
898
|
#
|
909
899
|
# see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/VirtualHosting.html
|
910
900
|
def get_link(bucket, key, expires=nil, headers={})
|
911
|
-
generate_link('GET', headers.merge(:url=>"#{bucket}/#{
|
901
|
+
generate_link('GET', headers.merge(:url=>"#{bucket}/#{AwsUtils::URLencode key}"), expires)
|
912
902
|
rescue
|
913
903
|
on_exception
|
914
904
|
end
|
@@ -918,7 +908,7 @@ module RightAws
|
|
918
908
|
# s3.head_link('my_awesome_bucket',key) #=> url string
|
919
909
|
#
|
920
910
|
def head_link(bucket, key, expires=nil, headers={})
|
921
|
-
generate_link('HEAD', headers.merge(:url=>"#{bucket}/#{
|
911
|
+
generate_link('HEAD', headers.merge(:url=>"#{bucket}/#{AwsUtils::URLencode key}"), expires)
|
922
912
|
rescue
|
923
913
|
on_exception
|
924
914
|
end
|
@@ -928,7 +918,7 @@ module RightAws
|
|
928
918
|
# s3.delete_link('my_awesome_bucket',key) #=> url string
|
929
919
|
#
|
930
920
|
def delete_link(bucket, key, expires=nil, headers={})
|
931
|
-
generate_link('DELETE', headers.merge(:url=>"#{bucket}/#{
|
921
|
+
generate_link('DELETE', headers.merge(:url=>"#{bucket}/#{AwsUtils::URLencode key}"), expires)
|
932
922
|
rescue
|
933
923
|
on_exception
|
934
924
|
end
|
@@ -939,7 +929,7 @@ module RightAws
|
|
939
929
|
# s3.get_acl_link('my_awesome_bucket',key) #=> url string
|
940
930
|
#
|
941
931
|
def get_acl_link(bucket, key='', headers={})
|
942
|
-
return generate_link('GET', headers.merge(:url=>"#{bucket}/#{
|
932
|
+
return generate_link('GET', headers.merge(:url=>"#{bucket}/#{AwsUtils::URLencode key}?acl"))
|
943
933
|
rescue
|
944
934
|
on_exception
|
945
935
|
end
|
@@ -949,7 +939,7 @@ module RightAws
|
|
949
939
|
# s3.put_acl_link('my_awesome_bucket',key) #=> url string
|
950
940
|
#
|
951
941
|
def put_acl_link(bucket, key='', headers={})
|
952
|
-
return generate_link('PUT', headers.merge(:url=>"#{bucket}/#{
|
942
|
+
return generate_link('PUT', headers.merge(:url=>"#{bucket}/#{AwsUtils::URLencode key}?acl"))
|
953
943
|
rescue
|
954
944
|
on_exception
|
955
945
|
end
|