right_aws 1.9.0 → 1.10.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/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
|