right_aws 2.0.0 → 2.1.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 +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)}
|