right_aws 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +22 -1
- data/Manifest.txt +11 -1
- data/README.txt +0 -4
- data/Rakefile +19 -25
- data/lib/acf/right_acf_interface.rb +199 -135
- data/lib/acf/right_acf_invalidations.rb +144 -0
- data/lib/acf/right_acf_origin_access_identities.rb +4 -4
- data/lib/acf/right_acf_streaming_interface.rb +19 -26
- data/lib/acw/right_acw_interface.rb +1 -2
- data/lib/as/right_as_interface.rb +6 -7
- data/lib/awsbase/right_awsbase.rb +287 -91
- data/lib/awsbase/support.rb +2 -82
- data/lib/awsbase/version.rb +9 -0
- data/lib/ec2/right_ec2.rb +101 -38
- data/lib/ec2/right_ec2_ebs.rb +71 -58
- data/lib/ec2/right_ec2_images.rb +82 -42
- data/lib/ec2/right_ec2_instances.rb +74 -44
- data/lib/ec2/right_ec2_placement_groups.rb +108 -0
- data/lib/ec2/right_ec2_reserved_instances.rb +50 -46
- data/lib/ec2/right_ec2_security_groups.rb +148 -32
- data/lib/ec2/right_ec2_spot_instances.rb +53 -27
- data/lib/ec2/right_ec2_tags.rb +139 -0
- data/lib/ec2/right_ec2_vpc.rb +151 -139
- data/lib/ec2/right_ec2_windows_mobility.rb +84 -0
- data/lib/elb/right_elb_interface.rb +93 -18
- data/lib/iam/right_iam_access_keys.rb +71 -0
- data/lib/iam/right_iam_groups.rb +195 -0
- data/lib/iam/right_iam_interface.rb +341 -0
- data/lib/iam/right_iam_mfa_devices.rb +67 -0
- data/lib/iam/right_iam_users.rb +251 -0
- data/lib/rds/right_rds_interface.rb +513 -202
- data/lib/right_aws.rb +12 -12
- data/lib/route_53/right_route_53_interface.rb +630 -0
- data/lib/s3/right_s3.rb +9 -12
- data/lib/s3/right_s3_interface.rb +10 -11
- data/lib/sdb/active_sdb.rb +18 -33
- data/lib/sdb/right_sdb_interface.rb +36 -4
- data/lib/sqs/right_sqs.rb +1 -2
- data/lib/sqs/right_sqs_gen2.rb +0 -1
- data/lib/sqs/right_sqs_gen2_interface.rb +4 -5
- data/lib/sqs/right_sqs_interface.rb +6 -7
- data/right_aws.gemspec +91 -0
- data/test/awsbase/test_helper.rb +2 -0
- data/test/awsbase/test_right_awsbase.rb +12 -0
- data/test/s3/test_right_s3.rb +1 -1
- data/test/sdb/test_active_sdb.rb +1 -1
- data/test/sdb/test_batch_put_attributes.rb +54 -0
- data/test/sqs/test_right_sqs.rb +0 -6
- data/test/sqs/test_right_sqs_gen2.rb +1 -1
- metadata +109 -58
@@ -0,0 +1,144 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2010 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
|
+
module RightAws
|
24
|
+
|
25
|
+
class AcfInterface
|
26
|
+
|
27
|
+
# List Invalidations
|
28
|
+
#
|
29
|
+
# acf.list_invalidations('E3LTBMK4EAQS7D') #=>
|
30
|
+
# [{:status=>"InProgress", :aws_id=>"I3AW9PPQS0CBKV"},
|
31
|
+
# {:status=>"InProgress", :aws_id=>"I1HV23N5KD3XH9"}]
|
32
|
+
#
|
33
|
+
def list_invalidations(distribution_aws_id)
|
34
|
+
result = []
|
35
|
+
incrementally_list_invalidations(distribution_aws_id) do |response|
|
36
|
+
result += response[:invalidations]
|
37
|
+
true
|
38
|
+
end
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
42
|
+
# Incrementally list Invalidations.
|
43
|
+
# Optional params: +:marker+ and +:max_items+.
|
44
|
+
#
|
45
|
+
def incrementally_list_invalidations(distribution_aws_id, params={}, &block)
|
46
|
+
opts = {}
|
47
|
+
opts['MaxItems'] = params[:max_items] if params[:max_items]
|
48
|
+
opts['Marker'] = params[:marker] if params[:marker]
|
49
|
+
last_response = nil
|
50
|
+
loop do
|
51
|
+
link = generate_request('GET', "distribution/#{distribution_aws_id}/invalidation", opts)
|
52
|
+
last_response = request_info(link, AcfInvalidationsListParser.new(:logger => @logger))
|
53
|
+
opts['Marker'] = last_response[:next_marker]
|
54
|
+
break unless block && block.call(last_response) && !last_response[:next_marker].right_blank?
|
55
|
+
end
|
56
|
+
last_response
|
57
|
+
end
|
58
|
+
|
59
|
+
#-----------------------------------------------------------------
|
60
|
+
# Origin Access Identity
|
61
|
+
#-----------------------------------------------------------------
|
62
|
+
|
63
|
+
# Create a new Invalidation batch.
|
64
|
+
#
|
65
|
+
# acf.create_invalidation('E3LTBMK4EAQS7D', :path => ['/boot.jpg', '/kd/boot.public.1.jpg']) #=>
|
66
|
+
# {:status=>"InProgress",
|
67
|
+
# :create_time=>"2010-12-08T14:03:38.449Z",
|
68
|
+
# :location=> "https://cloudfront.amazonaws.com/2010-11-01/distribution/E3LTBMK4EAQS7D/invalidation/I3AW9PPQS0CBKV",
|
69
|
+
# :aws_id=>"I3AW9PPQS0CBKV",
|
70
|
+
# :invalidation_batch=>
|
71
|
+
# {:caller_reference=>"201012081703372555972012",
|
72
|
+
# :path=>["/boot.jpg", "/kd/boot.public.1.jpg"]}}
|
73
|
+
#
|
74
|
+
def create_invalidation(distribution_aws_id, invalidation_batch)
|
75
|
+
invalidation_batch[:caller_reference] ||= generate_call_reference
|
76
|
+
link = generate_request('POST', "/distribution/#{distribution_aws_id}/invalidation", {}, invalidation_batch_to_xml(invalidation_batch))
|
77
|
+
merge_headers(request_info(link, AcfInvalidationsListParser.new(:logger => @logger))[:invalidations].first)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Get Invalidation
|
81
|
+
#
|
82
|
+
# acf.get_invalidation('E3LTBMK4EAQS7D', 'I3AW9PPQS0CBKV') #=>
|
83
|
+
# {:create_time=>"2010-12-08T14:03:38.449Z",
|
84
|
+
# :status=>"InProgress",
|
85
|
+
# :aws_id=>"I3AW9PPQS0CBKV",
|
86
|
+
# :invalidation_batch=>
|
87
|
+
# {:caller_reference=>"201012081703372555972012",
|
88
|
+
# :path=>["/boot.jpg", "/kd/boot.public.1.jpg"]}}
|
89
|
+
#
|
90
|
+
def get_invalidation(distribution_aws_id, aws_id)
|
91
|
+
link = generate_request('GET', "distribution/#{distribution_aws_id}/invalidation/#{aws_id}")
|
92
|
+
merge_headers(request_info(link, AcfInvalidationsListParser.new(:logger => @logger))[:invalidations].first)
|
93
|
+
end
|
94
|
+
|
95
|
+
#-----------------------------------------------------------------
|
96
|
+
# Batch
|
97
|
+
#-----------------------------------------------------------------
|
98
|
+
|
99
|
+
def invalidation_batch_to_xml(invalidation_batch) # :nodoc:
|
100
|
+
paths = ''
|
101
|
+
Array(invalidation_batch[:path]).each do |path|
|
102
|
+
paths << " <Path>#{AwsUtils::xml_escape(path)}</Path>\n"
|
103
|
+
end
|
104
|
+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
105
|
+
"<InvalidationBatch xmlns=\"http://#{@params[:server]}/doc/#{API_VERSION}/\">\n" +
|
106
|
+
" <CallerReference>#{invalidation_batch[:caller_reference]}</CallerReference>\n" +
|
107
|
+
paths +
|
108
|
+
"</InvalidationBatch>"
|
109
|
+
end
|
110
|
+
|
111
|
+
#-----------------------------------------------------------------
|
112
|
+
# PARSERS:
|
113
|
+
#-----------------------------------------------------------------
|
114
|
+
|
115
|
+
class AcfInvalidationsListParser < RightAWSParser # :nodoc:
|
116
|
+
def reset
|
117
|
+
@result = { :invalidations => [] }
|
118
|
+
end
|
119
|
+
def tagstart(name, attributes)
|
120
|
+
case name
|
121
|
+
when %r{(InvalidationSummary|Invalidation)$} then @item = {}
|
122
|
+
when %r{InvalidationBatch} then @item[:invalidation_batch] = {}
|
123
|
+
end
|
124
|
+
end
|
125
|
+
def tagend(name)
|
126
|
+
case name
|
127
|
+
when 'Marker' then @result[:marker] = @text
|
128
|
+
when 'NextMarker' then @result[:next_marker] = @text
|
129
|
+
when 'MaxItems' then @result[:max_items] = @text.to_i
|
130
|
+
when 'IsTruncated' then @result[:is_truncated] = (@text == 'true')
|
131
|
+
when 'Id' then @item[:aws_id] = @text
|
132
|
+
when 'Status' then @item[:status] = @text
|
133
|
+
when 'CreateTime' then @item[:create_time] = @text
|
134
|
+
when 'Path' then (@item[:invalidation_batch][:path] ||= []) << @text
|
135
|
+
when 'CallerReference' then @item[:invalidation_batch][:caller_reference] = @text
|
136
|
+
when %r{(InvalidationSummary|Invalidation)$}
|
137
|
+
@item[:invalidation_batch][:path].sort! if @item[:invalidation_batch] && !@item[:invalidation_batch][:path].right_blank?
|
138
|
+
@result[:invalidations] << @item
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
@@ -85,7 +85,7 @@ module RightAws
|
|
85
85
|
link = generate_request('GET', 'origin-access-identity/cloudfront', opts)
|
86
86
|
last_response = request_info(link, AcfOriginAccesIdentitiesListParser.new(:logger => @logger))
|
87
87
|
opts['Marker'] = last_response[:next_marker]
|
88
|
-
break unless block && block.call(last_response) && !last_response[:next_marker].
|
88
|
+
break unless block && block.call(last_response) && !last_response[:next_marker].right_blank?
|
89
89
|
end
|
90
90
|
last_response
|
91
91
|
end
|
@@ -159,7 +159,7 @@ module RightAws
|
|
159
159
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
160
160
|
"<CloudFrontOriginAccessIdentityConfig xmlns=\"http://#{@params[:server]}/doc/#{API_VERSION}/\">\n" +
|
161
161
|
" <CallerReference>#{config[:caller_reference]}</CallerReference>\n" +
|
162
|
-
" <Comment>#{
|
162
|
+
" <Comment>#{AwsUtils::xml_escape(config[:comment].to_s)}</Comment>\n" +
|
163
163
|
"</CloudFrontOriginAccessIdentityConfig>"
|
164
164
|
end
|
165
165
|
|
@@ -214,8 +214,8 @@ module RightAws
|
|
214
214
|
when 'IsTruncated' then @result[:is_truncated] = (@text == 'true')
|
215
215
|
when 'Id' then @item[:aws_id] = @text
|
216
216
|
when 'S3CanonicalUserId' then @item[:s3_canonical_user_id] = @text
|
217
|
-
when 'CallerReference' then @item[:caller_reference]
|
218
|
-
when 'Comment' then @item[:comment] =
|
217
|
+
when 'CallerReference' then @item[:caller_reference] = @text
|
218
|
+
when 'Comment' then @item[:comment] = AwsUtils::xml_unescape(@text)
|
219
219
|
end
|
220
220
|
case full_tag_name
|
221
221
|
when %r{CloudFrontOriginAccessIdentitySummary$},
|
@@ -40,7 +40,7 @@ module RightAws
|
|
40
40
|
# :aws_id=>"E3CWE2Z9USOS6B",
|
41
41
|
# :enabled=>true,
|
42
42
|
# :domain_name=>"s2jz1ourvss1fj.cloudfront.net",
|
43
|
-
# :
|
43
|
+
# :s3_origin=> {:dns_name=>"bucket-for-konstantin-00.s3.amazonaws.com"},
|
44
44
|
# :last_modified_time=>"2010-04-19T08:53:32.574Z",
|
45
45
|
# :comment=>"Woo-Hoo!",
|
46
46
|
# :cnames=>["stream.web.my-awesome-site.net"]},
|
@@ -49,7 +49,7 @@ module RightAws
|
|
49
49
|
# :aws_id=>"E3NPQZY4LKAYQ8",
|
50
50
|
# :enabled=>true,
|
51
51
|
# :domain_name=>"sw9nrsq9pudk3.cloudfront.net",
|
52
|
-
# :
|
52
|
+
# :s3_origin=> {:dns_name=>"bucket-for-konstantin-00.s3.amazonaws.com"},
|
53
53
|
# :last_modified_time=>"2010-04-19T08:59:09.600Z",
|
54
54
|
# :comment=>"Woo-Hoo!",
|
55
55
|
# :cnames=>["stream-6.web.my-awesome-site.net"]}]
|
@@ -78,7 +78,7 @@ module RightAws
|
|
78
78
|
# :enabled=>true,
|
79
79
|
# :last_modified_time=>"2010-04-19T08:53:32.574Z",
|
80
80
|
# :domain_name=>"s2jz1ourvss1fj.cloudfront.net",
|
81
|
-
# :
|
81
|
+
# :s3_origin=> {:dns_name=>"bucket-for-konstantin-00.s3.amazonaws.com"},
|
82
82
|
# :comment=>"Woo-Hoo!"}],
|
83
83
|
# :max_items=>1,
|
84
84
|
# :is_truncated=>true}
|
@@ -101,7 +101,7 @@ module RightAws
|
|
101
101
|
link = generate_request('GET', 'streaming-distribution', opts)
|
102
102
|
last_response = request_info(link, AcfDistributionListParser.new(:logger => @logger))
|
103
103
|
opts['Marker'] = last_response[:next_marker]
|
104
|
-
break unless block && block.call(last_response) && !last_response[:next_marker].
|
104
|
+
break unless block && block.call(last_response) && !last_response[:next_marker].right_blank?
|
105
105
|
end
|
106
106
|
last_response
|
107
107
|
end
|
@@ -118,26 +118,18 @@ module RightAws
|
|
118
118
|
# :e_tag=>"E2588L5QL4BLXH",
|
119
119
|
# :enabled=>true,
|
120
120
|
# :domain_name=>"s1di8imd85wgld.cloudfront.net",
|
121
|
-
# :
|
121
|
+
# :s3_origin=> {:dns_name=>"bucket-for-konstantin-00.s3.amazonaws.com"},
|
122
122
|
# :last_modified_time=>Mon Apr 19 08:54:42 UTC 2010,
|
123
123
|
# :location=>
|
124
124
|
# "https://cloudfront.amazonaws.com/streaming-distribution/E1M5LERJLU636F",
|
125
125
|
# :comment=>"Woo-Hoo!"}
|
126
126
|
#
|
127
|
-
def create_streaming_distribution(
|
128
|
-
config = { :origin => origin,
|
129
|
-
:comment => comment,
|
130
|
-
:enabled => enabled,
|
131
|
-
:cnames => Array(cnames),
|
132
|
-
:caller_reference => caller_reference }
|
133
|
-
create_streaming_distribution_by_config(config)
|
134
|
-
end
|
135
|
-
|
136
|
-
def create_streaming_distribution_by_config(config)
|
127
|
+
def create_streaming_distribution(config)
|
137
128
|
config[:caller_reference] ||= generate_call_reference
|
138
129
|
link = generate_request('POST', 'streaming-distribution', {}, streaming_distribution_config_to_xml(config))
|
139
130
|
merge_headers(request_info(link, AcfDistributionListParser.new(:logger => @logger))[:distributions].first)
|
140
131
|
end
|
132
|
+
alias_method :create_streaming_distribution_by_config, :create_streaming_distribution
|
141
133
|
|
142
134
|
# Get a streaming distribution's information.
|
143
135
|
# Returns a distribution's information or RightAws::AwsError exception.
|
@@ -149,7 +141,7 @@ module RightAws
|
|
149
141
|
# :aws_id=>"E3CWE2Z9USOS6B",
|
150
142
|
# :enabled=>true,
|
151
143
|
# :domain_name=>"s2jz1ourvss1fj.cloudfront.net",
|
152
|
-
# :
|
144
|
+
# :s3_origin=> {:dns_name=>"bucket-for-konstantin-00.s3.amazonaws.com"},
|
153
145
|
# :last_modified_time=>"2010-04-19T08:53:32.574Z",
|
154
146
|
# :comment=>"Woo-Hoo!",
|
155
147
|
# :caller_reference=>"201004191253311625537161"}
|
@@ -167,10 +159,11 @@ module RightAws
|
|
167
159
|
# :aws_id=>"E1M5LERJLU636F",
|
168
160
|
# :enabled=>false,
|
169
161
|
# :domain_name=>"s1di8imd85wgld.cloudfront.net",
|
170
|
-
# :
|
162
|
+
# :s3_origin=> {
|
163
|
+
# :dns_name=>"bucket-for-konstantin-00.s3.amazonaws.com",
|
164
|
+
# :origin_access_identity=>"origin-access-identity/cloudfront/E3JPJZ80ZBX24G"},
|
171
165
|
# :last_modified_time=>"2010-04-19T09:14:07.160Z",
|
172
166
|
# :comment=>"Olah-lah!",
|
173
|
-
# :origin_access_identity=>"origin-access-identity/cloudfront/E3JPJZ80ZBX24G",
|
174
167
|
# :caller_reference=>"201004191254412191173215"}
|
175
168
|
#
|
176
169
|
def get_streaming_distribution(aws_id)
|
@@ -186,9 +179,10 @@ module RightAws
|
|
186
179
|
# :e_tag=>"E2K6XD13RCJQ6E",
|
187
180
|
# :cnames=>["stream-1.web.my-awesome-site.net"],
|
188
181
|
# :enabled=>false,
|
189
|
-
# :
|
182
|
+
# :s3_origin=> {
|
183
|
+
# :dns_name=>"bucket-for-konstantin-00.s3.amazonaws.com",
|
184
|
+
# :origin_access_identity=>"origin-access-identity/cloudfront/E3JPJZ80ZBX24G",},
|
190
185
|
# :comment=>"Olah-lah!",
|
191
|
-
# :origin_access_identity=>"origin-access-identity/cloudfront/E3JPJZ80ZBX24G",
|
192
186
|
# :caller_reference=>"201004191254412191173215"}
|
193
187
|
#
|
194
188
|
def get_streaming_distribution_config(aws_id)
|
@@ -197,21 +191,20 @@ module RightAws
|
|
197
191
|
end
|
198
192
|
|
199
193
|
# Set a streaming distribution's configuration
|
200
|
-
# (the :origin and the :caller_reference cannot be changed).
|
201
194
|
# Returns +true+ on success or RightAws::AwsError exception.
|
202
195
|
#
|
203
196
|
# acf.get_streaming_distribution_config('E1M5LERJLU636F') #=>
|
204
197
|
# {:e_tag=>"E2588L5QL4BLXH",
|
205
198
|
# :cnames=>["stream-1.web.my-awesome-site.net"],
|
206
199
|
# :enabled=>true,
|
207
|
-
# :
|
200
|
+
# :s3_origin=> {:dns_name=>"bucket-for-konstantin-00.s3.amazonaws.com"},
|
208
201
|
# :comment=>"Woo-Hoo!",
|
209
202
|
# :caller_reference=>"201004191254412191173215"}
|
210
203
|
#
|
211
|
-
# config[:comment]
|
212
|
-
# config[:enabled]
|
213
|
-
# config[:origin_access_identity] = "origin-access-identity/cloudfront/E3JPJZ80ZBX24G"
|
214
|
-
# config[:trusted_signers]
|
204
|
+
# config[:comment] = 'Olah-lah!'
|
205
|
+
# config[:enabled] = false
|
206
|
+
# config[:s3_origin][:origin_access_identity] = "origin-access-identity/cloudfront/E3JPJZ80ZBX24G"
|
207
|
+
# config[:trusted_signers] = ['self', '648772220000', '120288270000']
|
215
208
|
#
|
216
209
|
# acf.set_distribution_config('E2REJM3VUN5RSI', config) #=> true
|
217
210
|
#
|
@@ -54,7 +54,6 @@ module RightAws
|
|
54
54
|
# * <tt>:server</tt>: ACW service host, default: DEFAULT_HOST
|
55
55
|
# * <tt>:port</tt>: ACW service port, default: DEFAULT_PORT
|
56
56
|
# * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL
|
57
|
-
# * <tt>:multi_thread</tt>: true=HTTP connection per thread, false=per process
|
58
57
|
# * <tt>:logger</tt>: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT
|
59
58
|
# * <tt>:signature_version</tt>: The signature version : '0','1' or '2'(default)
|
60
59
|
# * <tt>:cache</tt>: true/false(default): list_metrics
|
@@ -145,7 +144,7 @@ module RightAws
|
|
145
144
|
period = (options[:period] && options[:period].to_i) || 60
|
146
145
|
# Statistics ('Average' by default)
|
147
146
|
statistics = Array(options[:statistics]).flatten
|
148
|
-
statistics = statistics.
|
147
|
+
statistics = statistics.right_blank? ? ['Average'] : statistics.map{|statistic| statistic.to_s.capitalize }
|
149
148
|
# Times (5.min.ago up to now by default)
|
150
149
|
start_time = options[:start_time] || (Time.now.utc - 5*60)
|
151
150
|
start_time = start_time.utc.strftime("%Y-%m-%dT%H:%M:%S+00:00") if start_time.is_a?(Time)
|
@@ -95,7 +95,6 @@ module RightAws
|
|
95
95
|
# * <tt>:server</tt>: AS service host, default: DEFAULT_HOST
|
96
96
|
# * <tt>:port</tt>: AS service port, default: DEFAULT_PORT
|
97
97
|
# * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL
|
98
|
-
# * <tt>:multi_thread</tt>: true=HTTP connection per thread, false=per process
|
99
98
|
# * <tt>:logger</tt>: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT
|
100
99
|
# * <tt>:signature_version</tt>: The signature version : '0','1' or '2'(default)
|
101
100
|
# * <tt>:cache</tt>: true/false(default): describe_auto_scaling_groups
|
@@ -136,7 +135,7 @@ module RightAws
|
|
136
135
|
auto_scaling_group_names = auto_scaling_group_names.flatten.compact
|
137
136
|
request_hash = amazonize_list('AutoScalingGroupNames.member', auto_scaling_group_names)
|
138
137
|
link = generate_request("DescribeAutoScalingGroups", request_hash)
|
139
|
-
request_cache_or_info(:describe_auto_scaling_groups, link, DescribeAutoScalingGroupsParser, @@bench, auto_scaling_group_names.
|
138
|
+
request_cache_or_info(:describe_auto_scaling_groups, link, DescribeAutoScalingGroupsParser, @@bench, auto_scaling_group_names.right_blank?)
|
140
139
|
end
|
141
140
|
|
142
141
|
# Creates a new auto scaling group with the specified name.
|
@@ -154,7 +153,7 @@ module RightAws
|
|
154
153
|
options[:max_size] ||= 20
|
155
154
|
options[:cooldown] ||= 0
|
156
155
|
request_hash = amazonize_list('AvailabilityZones.member', availability_zones)
|
157
|
-
request_hash.merge!( amazonize_list('LoadBalancerNames', options[:load_balancer_names]) )
|
156
|
+
request_hash.merge!( amazonize_list('LoadBalancerNames.member', options[:load_balancer_names]) )
|
158
157
|
request_hash.merge!( 'AutoScalingGroupName' => auto_scaling_group_name,
|
159
158
|
'LaunchConfigurationName' => launch_configuration_name,
|
160
159
|
'MinSize' => options[:min_size],
|
@@ -302,7 +301,7 @@ module RightAws
|
|
302
301
|
link = generate_request("DescribeScalingActivities", request_hash)
|
303
302
|
last_response = request_info( link, DescribeScalingActivitiesParser.new(:logger => @logger))
|
304
303
|
request_hash['NextToken'] = last_response[:next_token]
|
305
|
-
break unless block && block.call(last_response) && !last_response[:next_token].
|
304
|
+
break unless block && block.call(last_response) && !last_response[:next_token].right_blank?
|
306
305
|
end
|
307
306
|
last_response
|
308
307
|
end
|
@@ -355,10 +354,10 @@ module RightAws
|
|
355
354
|
request_hash = { 'LaunchConfigurationName' => launch_configuration_name,
|
356
355
|
'ImageId' => image_id,
|
357
356
|
'InstanceType' => instance_type }
|
358
|
-
request_hash.merge!(amazonize_list('SecurityGroups.member', options[:security_groups])) unless options[:security_groups].
|
357
|
+
request_hash.merge!(amazonize_list('SecurityGroups.member', options[:security_groups])) unless options[:security_groups].right_blank?
|
359
358
|
request_hash.merge!(amazonize_block_device_mappings(options[:block_device_mappings], 'BlockDeviceMappings.member'))
|
360
359
|
request_hash['KeyName'] = options[:key_name] if options[:key_name]
|
361
|
-
request_hash['UserData'] = Base64.encode64(options[:user_data]).delete("\n") unless options[:user_data].
|
360
|
+
request_hash['UserData'] = Base64.encode64(options[:user_data]).delete("\n") unless options[:user_data].right_blank? if options[:user_data]
|
362
361
|
request_hash['KernelId'] = options[:kernel_id] if options[:kernel_id]
|
363
362
|
request_hash['RamdiskId'] = options[:ramdisk_id] if options[:ramdisk_id]
|
364
363
|
link = generate_request("CreateLaunchConfiguration", request_hash)
|
@@ -428,7 +427,7 @@ module RightAws
|
|
428
427
|
link = generate_request("DescribeLaunchConfigurations", request_hash)
|
429
428
|
last_response = request_info( link, DescribeLaunchConfigurationsParser.new(:logger => @logger) )
|
430
429
|
request_hash['NextToken'] = last_response[:next_token]
|
431
|
-
break unless block && block.call(last_response) && !last_response[:next_token].
|
430
|
+
break unless block && block.call(last_response) && !last_response[:next_token].right_blank?
|
432
431
|
end
|
433
432
|
last_response
|
434
433
|
end
|
@@ -23,7 +23,6 @@
|
|
23
23
|
|
24
24
|
# Test
|
25
25
|
module RightAws
|
26
|
-
# require 'md5'
|
27
26
|
require 'digest/md5'
|
28
27
|
require 'pp'
|
29
28
|
|
@@ -53,6 +52,14 @@ module RightAws
|
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
55
|
+
def self.xml_escape(text) # :nodoc:
|
56
|
+
REXML::Text::normalize(text)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.xml_unescape(text) # :nodoc:
|
60
|
+
REXML::Text::unnormalize(text)
|
61
|
+
end
|
62
|
+
|
56
63
|
# Set a timestamp and a signature version
|
57
64
|
def self.fix_service_params(service_hash, signature)
|
58
65
|
service_hash["Timestamp"] ||= utc_iso8601(Time.now) unless service_hash["Expires"]
|
@@ -136,6 +143,20 @@ module RightAws
|
|
136
143
|
params = items.last.kind_of?(Hash) ? items.pop : {}
|
137
144
|
[items, params]
|
138
145
|
end
|
146
|
+
|
147
|
+
# Generates a token in format of:
|
148
|
+
# 1. "1dd8d4e4-db6b-11df-b31d-0025b37efad0 (if UUID gem is loaded)
|
149
|
+
# 2. "1287483761-855215-zSv2z-bWGj2-31M5t-ags9m" (if UUID gem is not loaded)
|
150
|
+
TOKEN_GENERATOR_CHARSET = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
|
151
|
+
def self.generate_unique_token
|
152
|
+
time = Time.now
|
153
|
+
token = "%d-%06d" % [time.to_i, time.usec]
|
154
|
+
4.times do
|
155
|
+
token << "-"
|
156
|
+
5.times { token << TOKEN_GENERATOR_CHARSET[rand(TOKEN_GENERATOR_CHARSET.size)] }
|
157
|
+
end
|
158
|
+
token
|
159
|
+
end
|
139
160
|
end
|
140
161
|
|
141
162
|
class AwsBenchmarkingBlock #:nodoc:
|
@@ -181,7 +202,31 @@ module RightAws
|
|
181
202
|
def self.amazon_problems=(problems_list)
|
182
203
|
@@amazon_problems = problems_list
|
183
204
|
end
|
184
|
-
|
205
|
+
|
206
|
+
# Raise an exception if a timeout occures while an API call is in progress.
|
207
|
+
# This helps to avoid a duplicate resources creation when Amazon hangs for some time and
|
208
|
+
# RightHttpConnection is forced to use retries to get a response from it.
|
209
|
+
#
|
210
|
+
# If an API call action is in the list then no attempts to retry are performed.
|
211
|
+
#
|
212
|
+
RAISE_ON_TIMEOUT_ON_ACTIONS = %w{
|
213
|
+
AllocateAddress
|
214
|
+
CreateSnapshot
|
215
|
+
CreateVolume
|
216
|
+
PurchaseReservedInstancesOffering
|
217
|
+
RequestSpotInstances
|
218
|
+
RunInstances
|
219
|
+
}
|
220
|
+
@@raise_on_timeout_on_actions = RAISE_ON_TIMEOUT_ON_ACTIONS.dup
|
221
|
+
|
222
|
+
def self.raise_on_timeout_on_actions
|
223
|
+
@@raise_on_timeout_on_actions
|
224
|
+
end
|
225
|
+
|
226
|
+
def self.raise_on_timeout_on_actions=(actions_list)
|
227
|
+
@@raise_on_timeout_on_actions = actions_list
|
228
|
+
end
|
229
|
+
|
185
230
|
end
|
186
231
|
|
187
232
|
module RightAwsBaseInterface
|
@@ -222,9 +267,9 @@ module RightAws
|
|
222
267
|
@params = params
|
223
268
|
# If one defines EC2_URL he may forget to use a single slash as an "empty service" path.
|
224
269
|
# Amazon does not like this therefore add this bad boy if he is missing...
|
225
|
-
service_info[:default_service] = '/' if service_info[:default_service].
|
270
|
+
service_info[:default_service] = '/' if service_info[:default_service].right_blank?
|
226
271
|
raise AwsError.new("AWS access keys are required to operate on #{service_info[:name]}") \
|
227
|
-
if aws_access_key_id.
|
272
|
+
if aws_access_key_id.right_blank? || aws_secret_access_key.right_blank?
|
228
273
|
@aws_access_key_id = aws_access_key_id
|
229
274
|
@aws_secret_access_key = aws_secret_access_key
|
230
275
|
# if the endpoint was explicitly defined - then use it
|
@@ -233,7 +278,7 @@ module RightAws
|
|
233
278
|
@params[:port] = URI.parse(@params[:endpoint_url]).port
|
234
279
|
@params[:service] = URI.parse(@params[:endpoint_url]).path
|
235
280
|
# make sure the 'service' path is not empty
|
236
|
-
@params[:service] = service_info[:default_service] if @params[:service].
|
281
|
+
@params[:service] = service_info[:default_service] if @params[:service].right_blank?
|
237
282
|
@params[:protocol] = URI.parse(@params[:endpoint_url]).scheme
|
238
283
|
@params[:region] = nil
|
239
284
|
else
|
@@ -243,12 +288,15 @@ module RightAws
|
|
243
288
|
@params[:service] ||= service_info[:default_service]
|
244
289
|
@params[:protocol] ||= service_info[:default_protocol]
|
245
290
|
end
|
246
|
-
#
|
291
|
+
# a set of options to be passed to RightHttpConnection object
|
292
|
+
@params[:connection_options] = {} unless @params[:connection_options].is_a?(Hash)
|
293
|
+
@with_connection_options = {}
|
247
294
|
@params[:connections] ||= :shared # || :dedicated
|
248
295
|
@params[:max_connections] ||= 10
|
249
296
|
@params[:connection_lifetime] ||= 20*60
|
250
297
|
@params[:api_version] ||= service_info[:default_api_version]
|
251
298
|
@logger = @params[:logger]
|
299
|
+
@logger = ::Rails.logger if !@logger && defined?(::Rails) && ::Rails.respond_to?(:logger)
|
252
300
|
@logger = RAILS_DEFAULT_LOGGER if !@logger && defined?(RAILS_DEFAULT_LOGGER)
|
253
301
|
@logger = Logger.new(STDOUT) if !@logger
|
254
302
|
@logger.info "New #{self.class.name} using #{@params[:connections]} connections mode"
|
@@ -314,11 +362,57 @@ module RightAws
|
|
314
362
|
raise if $!.is_a?(AwsNoChange)
|
315
363
|
AwsError::on_aws_exception(self, options)
|
316
364
|
end
|
317
|
-
|
318
|
-
|
319
|
-
#
|
320
|
-
|
321
|
-
|
365
|
+
|
366
|
+
#----------------------------
|
367
|
+
# HTTP Connections handling
|
368
|
+
#----------------------------
|
369
|
+
|
370
|
+
def get_server_url(request) # :nodoc:
|
371
|
+
"#{request[:protocol]}://#{request[:server]}:#{request[:port]}"
|
372
|
+
end
|
373
|
+
|
374
|
+
def get_connections_storage(aws_service) # :nodoc:
|
375
|
+
case @params[:connections].to_s
|
376
|
+
when 'dedicated' then @connections_storage ||= {}
|
377
|
+
else Thread.current[aws_service] ||= {}
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
def destroy_connection(request, reason) # :nodoc:
|
382
|
+
connections = get_connections_storage(request[:aws_service])
|
383
|
+
server_url = get_server_url(request)
|
384
|
+
if connections[server_url]
|
385
|
+
connections[server_url][:connection].finish(reason)
|
386
|
+
connections.delete(server_url)
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
# Expire the connection if it has expired.
|
391
|
+
def get_connection(request) # :nodoc:
|
392
|
+
server_url = get_server_url(request)
|
393
|
+
connection_storage = get_connections_storage(request[:aws_service])
|
394
|
+
life_time_scratch = Time.now-@params[:connection_lifetime]
|
395
|
+
# Delete out-of-dated connections
|
396
|
+
connections_in_list = 0
|
397
|
+
connection_storage.to_a.sort{|conn1, conn2| conn2[1][:last_used_at] <=> conn1[1][:last_used_at]}.each do |serv_url, conn_opts|
|
398
|
+
if @params[:max_connections] <= connections_in_list
|
399
|
+
conn_opts[:connection].finish('out-of-limit')
|
400
|
+
connection_storage.delete(server_url)
|
401
|
+
elsif conn_opts[:last_used_at] < life_time_scratch
|
402
|
+
conn_opts[:connection].finish('out-of-date')
|
403
|
+
connection_storage.delete(server_url)
|
404
|
+
else
|
405
|
+
connections_in_list += 1
|
406
|
+
end
|
407
|
+
end
|
408
|
+
connection = (connection_storage[server_url] ||= {})
|
409
|
+
connection[:last_used_at] = Time.now
|
410
|
+
connection[:connection] ||= Rightscale::HttpConnection.new(:exception => RightAws::AwsError, :logger => @logger)
|
411
|
+
end
|
412
|
+
|
413
|
+
#----------------------------
|
414
|
+
# HTTP Requests handling
|
415
|
+
#----------------------------
|
322
416
|
|
323
417
|
# ACF, AMS, EC2, LBS and SDB uses this guy
|
324
418
|
# SQS and S3 use their own methods
|
@@ -354,43 +448,26 @@ module RightAws
|
|
354
448
|
raise "Unsupported HTTP verb #{verb.inspect}!"
|
355
449
|
end
|
356
450
|
# prepare output hash
|
357
|
-
{ :request => request,
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
#
|
366
|
-
|
367
|
-
|
368
|
-
@connections_storage ||= {}
|
369
|
-
else # 'dedicated'
|
370
|
-
@connections_storage = (Thread.current[aws_service] ||= {})
|
371
|
-
end
|
372
|
-
#
|
373
|
-
@connections_storage[server_url] ||= {}
|
374
|
-
@connections_storage[server_url][:last_used_at] = Time.now
|
375
|
-
@connections_storage[server_url][:connection] ||= Rightscale::HttpConnection.new(:exception => RightAws::AwsError, :logger => @logger)
|
376
|
-
# keep X most recent connections (but were used not far than Y minutes ago)
|
377
|
-
connections = 0
|
378
|
-
@connections_storage.to_a.sort{|i1, i2| i2[1][:last_used_at] <=> i1[1][:last_used_at]}.to_a.each do |i|
|
379
|
-
if i[0] != server_url && (@params[:max_connections] <= connections || i[1][:last_used_at] < Time.now - @params[:connection_lifetime])
|
380
|
-
# delete the connection from the list
|
381
|
-
@connections_storage.delete(i[0])
|
382
|
-
# then finish it
|
383
|
-
i[1][:connection].finish((@params[:max_connections] <= connections) ? "out-of-limit" : "out-of-date") rescue nil
|
384
|
-
else
|
385
|
-
connections += 1
|
386
|
-
end
|
451
|
+
request_hash = { :request => request,
|
452
|
+
:server => @params[:server],
|
453
|
+
:port => @params[:port],
|
454
|
+
:protocol => @params[:protocol] }
|
455
|
+
request_hash.merge!(@params[:connection_options])
|
456
|
+
request_hash.merge!(@with_connection_options)
|
457
|
+
|
458
|
+
# If an action is marked as "non-retryable" and there was no :raise_on_timeout option set
|
459
|
+
# explicitly then do set that option
|
460
|
+
if Array(RightAwsBase::raise_on_timeout_on_actions).include?(action) && !request_hash.has_key?(:raise_on_timeout)
|
461
|
+
request_hash.merge!(:raise_on_timeout => true)
|
387
462
|
end
|
388
|
-
|
463
|
+
|
464
|
+
request_hash
|
389
465
|
end
|
390
466
|
|
391
467
|
# All services uses this guy.
|
392
468
|
def request_info_impl(aws_service, benchblock, request, parser, &block) #:nodoc:
|
393
|
-
|
469
|
+
request[:aws_service] = aws_service
|
470
|
+
@connection = get_connection(request)
|
394
471
|
@last_request = request[:request]
|
395
472
|
@last_response = nil
|
396
473
|
response = nil
|
@@ -405,25 +482,31 @@ module RightAws
|
|
405
482
|
# Exceptions can originate from code directly in the block, or from user
|
406
483
|
# code called in the other block which is passed to response.read_body.
|
407
484
|
benchblock.service.add! do
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
response.read_body(&block)
|
415
|
-
else
|
416
|
-
@error_handler = AWSErrorHandler.new(self, parser, :errors_list => self.class.amazon_problems) unless @error_handler
|
417
|
-
check_result = @error_handler.check(request)
|
418
|
-
if check_result
|
485
|
+
begin
|
486
|
+
responsehdr = @connection.request(request) do |response|
|
487
|
+
#########
|
488
|
+
begin
|
489
|
+
@last_response = response
|
490
|
+
if response.is_a?(Net::HTTPSuccess)
|
419
491
|
@error_handler = nil
|
420
|
-
|
492
|
+
response.read_body(&block)
|
493
|
+
else
|
494
|
+
@error_handler = AWSErrorHandler.new(self, parser, :errors_list => self.class.amazon_problems) unless @error_handler
|
495
|
+
check_result = @error_handler.check(request)
|
496
|
+
if check_result
|
497
|
+
@error_handler = nil
|
498
|
+
return check_result
|
499
|
+
end
|
500
|
+
raise AwsError.new(@last_errors, @last_response.code, @last_request_id)
|
421
501
|
end
|
422
|
-
|
502
|
+
rescue Exception => e
|
503
|
+
blockexception = e
|
423
504
|
end
|
424
|
-
rescue Exception => e
|
425
|
-
blockexception = e
|
426
505
|
end
|
506
|
+
rescue Exception => e
|
507
|
+
# Kill a connection if we run into a low level connection error
|
508
|
+
destroy_connection(request, "error: #{e.message}")
|
509
|
+
raise e
|
427
510
|
end
|
428
511
|
#########
|
429
512
|
|
@@ -437,7 +520,15 @@ module RightAws
|
|
437
520
|
return parser.result
|
438
521
|
end
|
439
522
|
else
|
440
|
-
benchblock.service.add!
|
523
|
+
benchblock.service.add! do
|
524
|
+
begin
|
525
|
+
response = @connection.request(request)
|
526
|
+
rescue Exception => e
|
527
|
+
# Kill a connection if we run into a low level connection error
|
528
|
+
destroy_connection(request, "error: #{e.message}")
|
529
|
+
raise e
|
530
|
+
end
|
531
|
+
end
|
441
532
|
# check response for errors...
|
442
533
|
@last_response = response
|
443
534
|
if response.is_a?(Net::HTTPSuccess)
|
@@ -459,7 +550,7 @@ module RightAws
|
|
459
550
|
raise
|
460
551
|
end
|
461
552
|
|
462
|
-
def request_cache_or_info(method, link, parser_class, benchblock, use_cache=true) #:nodoc:
|
553
|
+
def request_cache_or_info(method, link, parser_class, benchblock, use_cache=true, &block) #:nodoc:
|
463
554
|
# We do not want to break the logic of parsing hence will use a dummy parser to process all the standard
|
464
555
|
# steps (errors checking etc). The dummy parser does nothig - just returns back the params it received.
|
465
556
|
# If the caching is enabled and hit then throw AwsNoChange.
|
@@ -469,7 +560,7 @@ module RightAws
|
|
469
560
|
cache_hits?(method.to_sym, response.body) if use_cache
|
470
561
|
parser = parser_class.new(:logger => @logger)
|
471
562
|
benchblock.xml.add!{ parser.parse(response, params) }
|
472
|
-
result =
|
563
|
+
result = block ? block.call(parser) : parser.result
|
473
564
|
# update parsed data
|
474
565
|
update_cache(method.to_sym, :parsed => result) if use_cache
|
475
566
|
result
|
@@ -480,7 +571,24 @@ module RightAws
|
|
480
571
|
@last_response && @last_response.body.to_s[%r{<requestId>(.+?)</requestId>}i] && $1
|
481
572
|
end
|
482
573
|
|
574
|
+
# Incrementally lists something.
|
575
|
+
def incrementally_list_items(action, parser_class, params={}, &block) # :nodoc:
|
576
|
+
params = params.dup
|
577
|
+
params['MaxItems'] = params.delete(:max_items) if params[:max_items]
|
578
|
+
params['Marker'] = params.delete(:marker) if params[:marker]
|
579
|
+
last_response = nil
|
580
|
+
loop do
|
581
|
+
last_response = request_info( generate_request(action, params), parser_class.new(:logger => @logger))
|
582
|
+
params['Marker'] = last_response[:marker]
|
583
|
+
break unless block && block.call(last_response) && !last_response[:marker].right_blank?
|
584
|
+
end
|
585
|
+
last_response
|
586
|
+
end
|
587
|
+
|
483
588
|
# Format array of items into Amazons handy hash ('?' is a place holder):
|
589
|
+
# Options:
|
590
|
+
# :default => "something" : Set a value to "something" when it is nil
|
591
|
+
# :default => :skip_nils : Skip nil values
|
484
592
|
#
|
485
593
|
# amazonize_list('Item', ['a', 'b', 'c']) =>
|
486
594
|
# { 'Item.1' => 'a', 'Item.2' => 'b', 'Item.3' => 'c' }
|
@@ -504,7 +612,7 @@ module RightAws
|
|
504
612
|
# "Filter.2.Key"=>"B",
|
505
613
|
# "Filter.2.Value.1"=>"ba",
|
506
614
|
# "Filter.2.Value.2"=>"bb"}
|
507
|
-
def amazonize_list(masks, list) #:nodoc:
|
615
|
+
def amazonize_list(masks, list, options={}) #:nodoc:
|
508
616
|
groups = {}
|
509
617
|
Array(list).each_with_index do |list_item, i|
|
510
618
|
Array(masks).each_with_index do |mask, mask_idx|
|
@@ -512,8 +620,16 @@ module RightAws
|
|
512
620
|
key.sub!('?', (i+1).to_s)
|
513
621
|
value = Array(list_item)[mask_idx]
|
514
622
|
if value.is_a?(Array)
|
515
|
-
groups.merge!(amazonize_list(key, value))
|
623
|
+
groups.merge!(amazonize_list(key, value, options))
|
516
624
|
else
|
625
|
+
if value.nil?
|
626
|
+
next if options[:default] == :skip_nils
|
627
|
+
value = options[:default]
|
628
|
+
end
|
629
|
+
# Hack to avoid having unhandled '?' in keys : do replace them all with '1':
|
630
|
+
# bad: ec2.amazonize_list(['Filter.?.Key', 'Filter.?.Value.?'], { a: => :b }) => {"Filter.1.Key"=>:a, "Filter.1.Value.?"=>1}
|
631
|
+
# good: ec2.amazonize_list(['Filter.?.Key', 'Filter.?.Value.?'], { a: => :b }) => {"Filter.1.Key"=>:a, "Filter.1.Value.1"=>1}
|
632
|
+
key.gsub!('?', '1')
|
517
633
|
groups[key] = value
|
518
634
|
end
|
519
635
|
end
|
@@ -531,7 +647,7 @@ module RightAws
|
|
531
647
|
|
532
648
|
def amazonize_block_device_mappings(block_device_mappings, key = 'BlockDeviceMapping') # :nodoc:
|
533
649
|
result = {}
|
534
|
-
unless block_device_mappings.
|
650
|
+
unless block_device_mappings.right_blank?
|
535
651
|
block_device_mappings = [block_device_mappings] unless block_device_mappings.is_a?(Array)
|
536
652
|
block_device_mappings.each_with_index do |b, idx|
|
537
653
|
BLOCK_DEVICE_KEY_MAPPING.each do |local_name, remote_name|
|
@@ -546,6 +662,64 @@ module RightAws
|
|
546
662
|
result
|
547
663
|
end
|
548
664
|
|
665
|
+
# Execute a block of code with custom set of settings for right_http_connection.
|
666
|
+
# Accepts next options (see Rightscale::HttpConnection for explanation):
|
667
|
+
# :raise_on_timeout
|
668
|
+
# :http_connection_retry_count
|
669
|
+
# :http_connection_open_timeout
|
670
|
+
# :http_connection_read_timeout
|
671
|
+
# :http_connection_retry_delay
|
672
|
+
# :user_agent
|
673
|
+
# :exception
|
674
|
+
#
|
675
|
+
# Example #1:
|
676
|
+
#
|
677
|
+
# # Try to create a snapshot but stop with exception if timeout is received
|
678
|
+
# # to avoid having a duplicate API calls that create duplicate snapshots.
|
679
|
+
# ec2 = Rightscale::Ec2::new(aws_access_key_id, aws_secret_access_key)
|
680
|
+
# ec2.with_connection_options(:raise_on_timeout => true) do
|
681
|
+
# ec2.create_snapshot('vol-898a6fe0', 'KD: WooHoo!!')
|
682
|
+
# end
|
683
|
+
#
|
684
|
+
# Example #2:
|
685
|
+
#
|
686
|
+
# # Opposite case when the setting is global:
|
687
|
+
# @ec2 = Rightscale::Ec2::new(aws_access_key_id, aws_secret_access_key,
|
688
|
+
# :connection_options => { :raise_on_timeout => true })
|
689
|
+
# # Create an SSHKey but do tries on timeout
|
690
|
+
# ec2.with_connection_options(:raise_on_timeout => false) do
|
691
|
+
# new_key = ec2.create_key_pair('my_test_key')
|
692
|
+
# end
|
693
|
+
#
|
694
|
+
# Example #3:
|
695
|
+
#
|
696
|
+
# # Global settings (HttpConnection level):
|
697
|
+
# Rightscale::HttpConnection::params[:http_connection_open_timeout] = 5
|
698
|
+
# Rightscale::HttpConnection::params[:http_connection_read_timeout] = 250
|
699
|
+
# Rightscale::HttpConnection::params[:http_connection_retry_count] = 2
|
700
|
+
#
|
701
|
+
# # Local setings (RightAws level)
|
702
|
+
# ec2 = Rightscale::Ec2::new(AWS_ID, AWS_KEY,
|
703
|
+
# :region => 'us-east-1',
|
704
|
+
# :connection_options => {
|
705
|
+
# :http_connection_read_timeout => 2,
|
706
|
+
# :http_connection_retry_count => 5,
|
707
|
+
# :user_agent => 'Mozilla 4.0'
|
708
|
+
# })
|
709
|
+
#
|
710
|
+
# # Custom settings (API call level)
|
711
|
+
# ec2.with_connection_options(:raise_on_timeout => true,
|
712
|
+
# :http_connection_read_timeout => 10,
|
713
|
+
# :user_agent => '') do
|
714
|
+
# pp ec2.describe_images
|
715
|
+
# end
|
716
|
+
#
|
717
|
+
def with_connection_options(options, &block)
|
718
|
+
@with_connection_options = options
|
719
|
+
block.call self
|
720
|
+
ensure
|
721
|
+
@with_connection_options = {}
|
722
|
+
end
|
549
723
|
end
|
550
724
|
|
551
725
|
|
@@ -666,7 +840,7 @@ module RightAws
|
|
666
840
|
@reiteration_delay = @@reiteration_start_delay
|
667
841
|
@retries = 0
|
668
842
|
# close current HTTP(S) connection on 5xx, errors from list and 4xx errors
|
669
|
-
@close_on_error = params[:close_on_error].nil? ? @@close_on_error : params[:close_on_error]
|
843
|
+
@close_on_error = params[:close_on_error].nil? ? @@close_on_error : params[:close_on_error]
|
670
844
|
@close_on_4xx_probability = params[:close_on_4xx_probability] || @@close_on_4xx_probability
|
671
845
|
end
|
672
846
|
|
@@ -709,10 +883,17 @@ module RightAws
|
|
709
883
|
# Ok, it is a redirect, find the new destination location
|
710
884
|
if redirect_detected
|
711
885
|
location = response['location']
|
886
|
+
# As for 301 ( Moved Permanently) Amazon does not return a 'Location' header but
|
887
|
+
# it is possible to extract a new endpoint from the response body
|
888
|
+
if location.right_blank? && response.code=='301' && response.body
|
889
|
+
new_endpoint = response.body[/<Endpoint>(.*?)<\/Endpoint>/] && $1
|
890
|
+
location = "#{request[:protocol]}://#{new_endpoint}:#{request[:port]}#{request[:request].path}"
|
891
|
+
end
|
712
892
|
# ... log information and ...
|
713
893
|
@aws.logger.info("##### #{@aws.class.name} redirect requested: #{response.code} #{response.message} #####")
|
714
894
|
@aws.logger.info(" Old location: #{request_text_data}")
|
715
895
|
@aws.logger.info(" New location: #{location}")
|
896
|
+
@aws.logger.info(" Request Verb: #{request[:request].class.name}")
|
716
897
|
# ... fix the connection data
|
717
898
|
request[:server] = URI.parse(location).host
|
718
899
|
request[:protocol] = URI.parse(location).scheme
|
@@ -735,7 +916,7 @@ module RightAws
|
|
735
916
|
# It may have a chance that one server is a semi-down and reconnection
|
736
917
|
# will help us to connect to the other server
|
737
918
|
if !redirect_detected && @close_on_error
|
738
|
-
@aws.
|
919
|
+
@aws.destroy_connection(request, "#{self.class.name}: error match to pattern '#{error_match}'")
|
739
920
|
end
|
740
921
|
|
741
922
|
if (Time.now < @stop_at)
|
@@ -764,13 +945,13 @@ module RightAws
|
|
764
945
|
end
|
765
946
|
# aha, this is unhandled error:
|
766
947
|
elsif @close_on_error
|
767
|
-
#
|
768
|
-
if @aws.last_response.code.to_s[/^5\d\d$/]
|
769
|
-
@aws.
|
948
|
+
# On 5xx(Server errors), 403(RequestTimeTooSkewed) and 408(Request Timeout) a conection has to be closed
|
949
|
+
if @aws.last_response.code.to_s[/^(5\d\d|403|408)$/]
|
950
|
+
@aws.destroy_connection(request, "#{self.class.name}: code: #{@aws.last_response.code}: '#{@aws.last_response.message}'")
|
770
951
|
# Is this a 4xx error ?
|
771
952
|
elsif @aws.last_response.code.to_s[/^4\d\d$/] && @close_on_4xx_probability > rand(100)
|
772
|
-
@aws.
|
773
|
-
|
953
|
+
@aws.destroy_connection(request, "#{self.class.name}: code: #{@aws.last_response.code}: '#{@aws.last_response.message}', " +
|
954
|
+
"probability: #{@close_on_4xx_probability}%")
|
774
955
|
end
|
775
956
|
end
|
776
957
|
result
|
@@ -781,29 +962,41 @@ module RightAws
|
|
781
962
|
|
782
963
|
#-----------------------------------------------------------------
|
783
964
|
|
784
|
-
class
|
785
|
-
def self.include_callback
|
786
|
-
include XML::SaxParser::Callbacks
|
787
|
-
end
|
965
|
+
class RightSaxParserCallbackTemplate #:nodoc:
|
788
966
|
def initialize(right_aws_parser)
|
789
967
|
@right_aws_parser = right_aws_parser
|
790
968
|
end
|
791
|
-
def on_start_element(name, attr_hash)
|
792
|
-
@right_aws_parser.tag_start(name, attr_hash)
|
793
|
-
end
|
794
969
|
def on_characters(chars)
|
795
970
|
@right_aws_parser.text(chars)
|
796
971
|
end
|
797
|
-
def on_end_element(name)
|
798
|
-
@right_aws_parser.tag_end(name)
|
799
|
-
end
|
800
972
|
def on_start_document; end
|
801
973
|
def on_comment(msg); end
|
802
974
|
def on_processing_instruction(target, data); end
|
803
975
|
def on_cdata_block(cdata); end
|
804
976
|
def on_end_document; end
|
805
977
|
end
|
806
|
-
|
978
|
+
|
979
|
+
class RightSaxParserCallback < RightSaxParserCallbackTemplate
|
980
|
+
def self.include_callback
|
981
|
+
include XML::SaxParser::Callbacks
|
982
|
+
end
|
983
|
+
def on_start_element(name, attr_hash)
|
984
|
+
@right_aws_parser.tag_start(name, attr_hash)
|
985
|
+
end
|
986
|
+
def on_end_element(name)
|
987
|
+
@right_aws_parser.tag_end(name)
|
988
|
+
end
|
989
|
+
end
|
990
|
+
|
991
|
+
class RightSaxParserCallbackNs < RightSaxParserCallbackTemplate
|
992
|
+
def on_start_element_ns(name, attr_hash, prefix, uri, namespaces)
|
993
|
+
@right_aws_parser.tag_start(name, attr_hash)
|
994
|
+
end
|
995
|
+
def on_end_element_ns(name, prefix, uri)
|
996
|
+
@right_aws_parser.tag_end(name)
|
997
|
+
end
|
998
|
+
end
|
999
|
+
|
807
1000
|
class RightAWSParser #:nodoc:
|
808
1001
|
# default parsing library
|
809
1002
|
DEFAULT_XML_LIBRARY = 'rexml'
|
@@ -864,32 +1057,35 @@ module RightAws
|
|
864
1057
|
if @xml_lib=='libxml' && !defined?(XML::SaxParser)
|
865
1058
|
begin
|
866
1059
|
require 'xml/libxml'
|
867
|
-
#
|
868
|
-
if XML::Parser::VERSION >= '0.5.1
|
869
|
-
|
1060
|
+
# Setup SaxParserCallback
|
1061
|
+
if XML::Parser::VERSION >= '0.5.1' &&
|
1062
|
+
XML::Parser::VERSION < '0.9.7'
|
1063
|
+
RightSaxParserCallback.include_callback
|
870
1064
|
end
|
871
1065
|
rescue LoadError => e
|
872
|
-
@@supported_xml_libs.delete(@xml_lib)
|
873
|
-
@xml_lib = DEFAULT_XML_LIBRARY
|
1066
|
+
@@supported_xml_libs.delete(@xml_lib)
|
1067
|
+
@xml_lib = DEFAULT_XML_LIBRARY
|
874
1068
|
if @logger
|
875
1069
|
@logger.error e.inspect
|
876
1070
|
@logger.error e.backtrace
|
877
|
-
@logger.info "Can not load 'libxml' library. '#{DEFAULT_XML_LIBRARY}' is used for parsing."
|
1071
|
+
@logger.info "Can not load 'libxml' library. '#{DEFAULT_XML_LIBRARY}' is used for parsing."
|
878
1072
|
end
|
879
1073
|
end
|
880
1074
|
end
|
881
1075
|
# Parse the xml text
|
882
1076
|
case @xml_lib
|
883
|
-
when 'libxml'
|
1077
|
+
when 'libxml'
|
884
1078
|
if XML::Parser::VERSION >= '0.9.9'
|
885
1079
|
# avoid warning on every usage
|
886
1080
|
xml = XML::SaxParser.string(xml_text)
|
887
1081
|
else
|
888
|
-
xml = XML::SaxParser.new
|
1082
|
+
xml = XML::SaxParser.new
|
889
1083
|
xml.string = xml_text
|
890
1084
|
end
|
891
1085
|
# check libxml-ruby version
|
892
|
-
if
|
1086
|
+
if XML::Parser::VERSION >= '0.9.7'
|
1087
|
+
xml.callbacks = RightSaxParserCallbackNs.new(self)
|
1088
|
+
elsif XML::Parser::VERSION >= '0.5.1'
|
893
1089
|
xml.callbacks = RightSaxParserCallback.new(self)
|
894
1090
|
else
|
895
1091
|
xml.on_start_element{|name, attr_hash| self.tag_start(name, attr_hash)}
|