icehouse-right_aws 1.11.0 → 2.2.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 +93 -15
- data/Manifest.txt +15 -1
- data/README.txt +0 -4
- data/Rakefile +34 -17
- data/lib/acf/right_acf_interface.rb +260 -124
- data/lib/acf/right_acf_invalidations.rb +144 -0
- data/lib/acf/right_acf_origin_access_identities.rb +230 -0
- data/lib/acf/right_acf_streaming_interface.rb +229 -0
- data/lib/acw/right_acw_interface.rb +4 -5
- data/lib/as/right_as_interface.rb +59 -51
- data/lib/awsbase/benchmark_fix.rb +0 -0
- data/lib/awsbase/right_awsbase.rb +351 -104
- data/lib/awsbase/support.rb +2 -82
- data/lib/awsbase/version.rb +9 -0
- data/lib/ec2/right_ec2.rb +97 -246
- data/lib/ec2/right_ec2_ebs.rb +88 -68
- data/lib/ec2/right_ec2_images.rb +90 -50
- data/lib/ec2/right_ec2_instances.rb +118 -89
- data/lib/ec2/right_ec2_placement_groups.rb +108 -0
- data/lib/ec2/right_ec2_reserved_instances.rb +51 -44
- data/lib/ec2/right_ec2_security_groups.rb +396 -0
- data/lib/ec2/right_ec2_spot_instances.rb +425 -0
- data/lib/ec2/right_ec2_tags.rb +139 -0
- data/lib/ec2/right_ec2_vpc.rb +152 -140
- data/lib/ec2/right_ec2_windows_mobility.rb +84 -0
- data/lib/elb/right_elb_interface.rb +205 -39
- 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 +591 -205
- data/lib/right_aws.rb +16 -12
- data/lib/route_53/right_route_53_interface.rb +640 -0
- data/lib/s3/right_s3.rb +34 -13
- data/lib/s3/right_s3_interface.rb +17 -14
- data/lib/sdb/active_sdb.rb +215 -38
- data/lib/sdb/right_sdb_interface.rb +93 -12
- 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 +9 -9
- data/lib/sqs/right_sqs_interface.rb +6 -7
- data/right_aws.gemspec +91 -0
- data/test/README.mdown +39 -0
- data/test/acf/test_helper.rb +0 -0
- data/test/acf/test_right_acf.rb +10 -18
- data/test/awsbase/test_helper.rb +0 -0
- data/test/awsbase/test_right_awsbase.rb +0 -1
- data/test/ec2/test_helper.rb +0 -0
- data/test/ec2/test_right_ec2.rb +0 -1
- data/test/elb/test_helper.rb +2 -0
- data/test/elb/test_right_elb.rb +43 -0
- data/test/http_connection.rb +0 -0
- data/test/route_53/fixtures/a_record.xml +18 -0
- data/test/route_53/fixtures/alias_record.xml +18 -0
- data/test/route_53/test_helper.rb +2 -0
- data/test/route_53/test_right_route_53.rb +141 -0
- data/test/s3/test_helper.rb +0 -0
- data/test/s3/test_right_s3.rb +11 -9
- data/test/s3/test_right_s3_stubbed.rb +6 -4
- data/test/sdb/test_active_sdb.rb +71 -13
- data/test/sdb/test_batch_put_attributes.rb +54 -0
- data/test/sdb/test_helper.rb +0 -0
- data/test/sdb/test_right_sdb.rb +13 -7
- data/test/sqs/test_helper.rb +0 -0
- data/test/sqs/test_right_sqs.rb +0 -6
- data/test/sqs/test_right_sqs_gen2.rb +22 -34
- data/test/test_credentials.rb +0 -0
- data/test/ts_right_aws.rb +0 -0
- metadata +146 -16
- data/VERSION +0 -1
@@ -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
|
@@ -144,8 +143,8 @@ module RightAws
|
|
144
143
|
# Period (60 sec by default)
|
145
144
|
period = (options[:period] && options[:period].to_i) || 60
|
146
145
|
# Statistics ('Average' by default)
|
147
|
-
statistics = options[:statistics].
|
148
|
-
statistics = statistics.
|
146
|
+
statistics = Array(options[:statistics]).flatten
|
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)
|
@@ -167,7 +166,7 @@ module RightAws
|
|
167
166
|
# dimentions
|
168
167
|
dim = []
|
169
168
|
dimentions.each do |key, values|
|
170
|
-
values.
|
169
|
+
Array(values).each { |value| dim << [key, value] }
|
171
170
|
end
|
172
171
|
request_hash.merge!(amazonize_list(['Dimensions.member.?.Name', 'Dimensions.member.?.Value'], dim))
|
173
172
|
#
|
@@ -199,7 +198,7 @@ module RightAws
|
|
199
198
|
end
|
200
199
|
def tagend(name)
|
201
200
|
case name
|
202
|
-
when 'Timestamp' then @item[:timestamp] =
|
201
|
+
when 'Timestamp' then @item[:timestamp] = @text
|
203
202
|
when 'Unit' then @item[:unit] = @text
|
204
203
|
when 'CustomUnit' then @item[:custom_unit] = @text
|
205
204
|
when 'Samples' then @item[:samples] = @text.to_f
|
@@ -51,7 +51,7 @@ module RightAws
|
|
51
51
|
# as.create_or_update_scaling_trigger('kd.tr.1', 'CentOS.5.1-c-array',
|
52
52
|
# :measure_name => 'CPUUtilization',
|
53
53
|
# :statistic => :average,
|
54
|
-
# :
|
54
|
+
# :dimensions => {
|
55
55
|
# 'AutoScalingGroupName' => 'CentOS.5.1-c-array',
|
56
56
|
# 'Namespace' => 'AWS',
|
57
57
|
# 'Service' => 'EC2' },
|
@@ -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.
|
@@ -153,8 +152,8 @@ module RightAws
|
|
153
152
|
options[:min_size] ||= 1
|
154
153
|
options[:max_size] ||= 20
|
155
154
|
options[:cooldown] ||= 0
|
156
|
-
request_hash = amazonize_list('AvailabilityZones.member', availability_zones
|
157
|
-
request_hash.merge!( amazonize_list('LoadBalancerNames', options[:load_balancer_names]
|
155
|
+
request_hash = amazonize_list('AvailabilityZones.member', availability_zones)
|
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],
|
@@ -204,7 +203,7 @@ module RightAws
|
|
204
203
|
# as.update_auto_scaling_group('CentOS.5.1-c', :min_size => 1, :max_size => 4) #=> true
|
205
204
|
#
|
206
205
|
def update_auto_scaling_group(auto_scaling_group_name, options={})
|
207
|
-
request_hash = amazonize_list('AvailabilityZones.member', options[:availability_zones]
|
206
|
+
request_hash = amazonize_list('AvailabilityZones.member', options[:availability_zones])
|
208
207
|
request_hash['AutoScalingGroupName'] = auto_scaling_group_name
|
209
208
|
request_hash['LaunchConfigurationName'] = options[:launch_configuration_name] if options[:launch_configuration_name]
|
210
209
|
request_hash['MinSize'] = options[:min_size] if 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
|
@@ -339,21 +338,26 @@ module RightAws
|
|
339
338
|
# Options: +:security_groups+, +:block_device_mappings+, +:key_name+,
|
340
339
|
# +:user_data+, +:kernel_id+, +:ramdisk_id+
|
341
340
|
#
|
342
|
-
# as.create_launch_configuration('CentOS.5.1-c', 'ami-08f41161', '
|
343
|
-
#
|
344
|
-
#
|
345
|
-
#
|
341
|
+
# as.create_launch_configuration('kd: CentOS.5.1-c.1', 'ami-08f41161', 'c1.medium',
|
342
|
+
# :key_name => 'tim',
|
343
|
+
# :security_groups => ['default'],
|
344
|
+
# :user_data => "Woohoo: CentOS.5.1-c",
|
345
|
+
# :block_device_mappings => [ { :device_name => '/dev/sdk',
|
346
|
+
# :ebs_snapshot_id => 'snap-145cbc7d',
|
347
|
+
# :ebs_delete_on_termination => true,
|
348
|
+
# :ebs_volume_size => 3,
|
349
|
+
# :virtual_name => 'ephemeral2'
|
350
|
+
# } ]
|
351
|
+
# ) #=> true
|
346
352
|
#
|
347
353
|
def create_launch_configuration(launch_configuration_name, image_id, instance_type, options={})
|
348
|
-
availability_zones = availability_zones.to_a
|
349
354
|
request_hash = { 'LaunchConfigurationName' => launch_configuration_name,
|
350
355
|
'ImageId' => image_id,
|
351
356
|
'InstanceType' => instance_type }
|
352
|
-
request_hash.merge!(amazonize_list('SecurityGroups.member', options[:security_groups])) unless options[:security_groups].
|
353
|
-
request_hash.merge!(
|
354
|
-
options[:block_device_mappings].to_a)) unless options[:block_device_mappings].blank?
|
357
|
+
request_hash.merge!(amazonize_list('SecurityGroups.member', options[:security_groups])) unless options[:security_groups].right_blank?
|
358
|
+
request_hash.merge!(amazonize_block_device_mappings(options[:block_device_mappings], 'BlockDeviceMappings.member'))
|
355
359
|
request_hash['KeyName'] = options[:key_name] if options[:key_name]
|
356
|
-
request_hash['UserData'] = options[:user_data]
|
360
|
+
request_hash['UserData'] = Base64.encode64(options[:user_data]).delete("\n") unless options[:user_data].right_blank? if options[:user_data]
|
357
361
|
request_hash['KernelId'] = options[:kernel_id] if options[:kernel_id]
|
358
362
|
request_hash['RamdiskId'] = options[:ramdisk_id] if options[:ramdisk_id]
|
359
363
|
link = generate_request("CreateLaunchConfiguration", request_hash)
|
@@ -365,16 +369,17 @@ module RightAws
|
|
365
369
|
# Returns an array of configurations.
|
366
370
|
#
|
367
371
|
# as.describe_launch_configurations #=>
|
368
|
-
# [{:
|
369
|
-
# :kernel_id=>"",
|
370
|
-
# :launch_configuration_name=>"CentOS.5.1-c",
|
372
|
+
# [{:security_groups=>["default"],
|
371
373
|
# :ramdisk_id=>"",
|
372
|
-
# :
|
373
|
-
# :
|
374
|
-
# :
|
374
|
+
# :user_data=>"V29vaG9vOiBDZW50T1MuNS4xLWM=",
|
375
|
+
# :instance_type=>"c1.medium",
|
376
|
+
# :block_device_mappings=>
|
377
|
+
# [{:virtual_name=>"ephemeral2", :device_name=>"/dev/sdk"}],
|
378
|
+
# :launch_configuration_name=>"kd: CentOS.5.1-c.1",
|
379
|
+
# :created_time=>"2010-03-29T10:00:32.742Z",
|
375
380
|
# :image_id=>"ami-08f41161",
|
376
|
-
# :
|
377
|
-
# :
|
381
|
+
# :key_name=>"tim",
|
382
|
+
# :kernel_id=>""}, ...]
|
378
383
|
#
|
379
384
|
def describe_launch_configurations(*launch_configuration_names)
|
380
385
|
result = []
|
@@ -422,7 +427,7 @@ module RightAws
|
|
422
427
|
link = generate_request("DescribeLaunchConfigurations", request_hash)
|
423
428
|
last_response = request_info( link, DescribeLaunchConfigurationsParser.new(:logger => @logger) )
|
424
429
|
request_hash['NextToken'] = last_response[:next_token]
|
425
|
-
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?
|
426
431
|
end
|
427
432
|
last_response
|
428
433
|
end
|
@@ -449,12 +454,12 @@ module RightAws
|
|
449
454
|
# Returns +true+ or an exception.
|
450
455
|
#
|
451
456
|
# Options: +:measure_name+, +:statistic+, +:period+, +:lower_threshold+, +:lower_breach_scale_increment+,
|
452
|
-
# +:upper_threshold+, +:upper_breach_scale_increment+, +:
|
457
|
+
# +:upper_threshold+, +:upper_breach_scale_increment+, +:dimensions+, +:breach_duration+, +:unit+, +:custom_unit+
|
453
458
|
#
|
454
459
|
# as.create_or_update_scaling_trigger('kd.tr.1', 'CentOS.5.1-c-array',
|
455
460
|
# :measure_name => 'CPUUtilization',
|
456
461
|
# :statistic => :average,
|
457
|
-
# :
|
462
|
+
# :dimensions => {
|
458
463
|
# 'AutoScalingGroupName' => 'CentOS.5.1-c-array',
|
459
464
|
# 'Namespace' => 'AWS',
|
460
465
|
# 'Service' => 'EC2' },
|
@@ -478,11 +483,11 @@ module RightAws
|
|
478
483
|
'BreachDuration' => options[:breach_duration] }
|
479
484
|
request_hash['Unit'] = options[:unit] if options[:unit]
|
480
485
|
request_hash['CustomUnit'] = options[:custom_unit] if options[:custom_unit]
|
481
|
-
|
482
|
-
(options[:
|
483
|
-
values.
|
486
|
+
dimensions = []
|
487
|
+
(options[:dimensions] || {}).each do |key, values|
|
488
|
+
Array(values).each { |value| dimensions << [key, value] }
|
484
489
|
end
|
485
|
-
request_hash.merge!(amazonize_list(['Dimensions.member.?.Name', 'Dimensions.member.?.Value'],
|
490
|
+
request_hash.merge!(amazonize_list(['Dimensions.member.?.Name', 'Dimensions.member.?.Value'], dimensions))
|
486
491
|
link = generate_request("CreateOrUpdateScalingTrigger", request_hash)
|
487
492
|
request_info(link, RightHttp2xxParser.new(:logger => @logger))
|
488
493
|
end
|
@@ -537,8 +542,8 @@ module RightAws
|
|
537
542
|
def tagend(name)
|
538
543
|
case name
|
539
544
|
when 'ActivityId' then @item[:activity_id] = @text
|
540
|
-
when 'StartTime' then @item[:start_time] =
|
541
|
-
when 'EndTime' then @item[:end_time] =
|
545
|
+
when 'StartTime' then @item[:start_time] = @text
|
546
|
+
when 'EndTime' then @item[:end_time] = @text
|
542
547
|
when 'Progress' then @item[:progress] = @text.to_i
|
543
548
|
when 'StatusCode' then @item[:status_code] = @text
|
544
549
|
when 'Cause' then @item[:cause] = @text
|
@@ -570,7 +575,7 @@ module RightAws
|
|
570
575
|
end
|
571
576
|
def tagend(name)
|
572
577
|
case name
|
573
|
-
when 'CreatedTime' then @item[:created_time] =
|
578
|
+
when 'CreatedTime' then @item[:created_time] = @text
|
574
579
|
when 'MinSize' then @item[:min_size] = @text.to_i
|
575
580
|
when 'MaxSize' then @item[:max_size] = @text.to_i
|
576
581
|
when 'DesiredCapacity' then @item[:desired_capacity] = @text.to_i
|
@@ -603,18 +608,17 @@ module RightAws
|
|
603
608
|
|
604
609
|
class DescribeLaunchConfigurationsParser < RightAWSParser #:nodoc:
|
605
610
|
def tagstart(name, attributes)
|
606
|
-
case
|
607
|
-
when
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
end
|
611
|
+
case full_tag_name
|
612
|
+
when %r{/LaunchConfigurations/member$}
|
613
|
+
@item = { :block_device_mappings => [],
|
614
|
+
:security_groups => [] }
|
615
|
+
when %r{/BlockDeviceMappings/member$}
|
616
|
+
@block_device_mapping = {}
|
613
617
|
end
|
614
618
|
end
|
615
619
|
def tagend(name)
|
616
620
|
case name
|
617
|
-
when 'CreatedTime' then @item[:created_time] =
|
621
|
+
when 'CreatedTime' then @item[:created_time] = @text
|
618
622
|
when 'InstanceType' then @item[:instance_type] = @text
|
619
623
|
when 'KeyName' then @item[:key_name] = @text
|
620
624
|
when 'ImageId' then @item[:image_id] = @text
|
@@ -622,20 +626,24 @@ module RightAws
|
|
622
626
|
when 'RamdiskId' then @item[:ramdisk_id] = @text
|
623
627
|
when 'LaunchConfigurationName' then @item[:launch_configuration_name] = @text
|
624
628
|
when 'UserData' then @item[:user_data] = @text
|
625
|
-
when '
|
626
|
-
|
627
|
-
|
628
|
-
when
|
629
|
-
|
630
|
-
@
|
629
|
+
when 'NextToken' then @result[:next_token] = @text
|
630
|
+
else
|
631
|
+
case full_tag_name
|
632
|
+
when %r{/BlockDeviceMappings/member} # no trailing $
|
633
|
+
case name
|
634
|
+
when 'DeviceName' then @block_device_mapping[:device_name] = @text
|
635
|
+
when 'VirtualName' then @block_device_mapping[:virtual_name] = @text
|
636
|
+
when 'member' then @item[:block_device_mappings] << @block_device_mapping
|
637
|
+
end
|
638
|
+
when %r{member/SecurityGroups/member$}
|
639
|
+
@item[:security_groups] << @text
|
640
|
+
when %r{/LaunchConfigurations/member$}
|
631
641
|
@item[:security_groups].sort!
|
632
642
|
@result[:launch_configurations] << @item
|
633
643
|
end
|
634
|
-
when 'NextToken' then @result[:next_token] = @text
|
635
644
|
end
|
636
645
|
end
|
637
646
|
def reset
|
638
|
-
@p = 'DescribeLaunchConfigurationsResponse/DescribeLaunchConfigurationsResult/LaunchConfigurations'
|
639
647
|
@result = { :launch_configurations => []}
|
640
648
|
end
|
641
649
|
end
|
@@ -660,7 +668,7 @@ module RightAws
|
|
660
668
|
case name
|
661
669
|
when 'AutoScalingGroupName' then @item[:auto_scaling_group_name] = @text
|
662
670
|
when 'MeasureName' then @item[:measure_name] = @text
|
663
|
-
when 'CreatedTime' then @item[:created_time] =
|
671
|
+
when 'CreatedTime' then @item[:created_time] = @text
|
664
672
|
when 'BreachDuration' then @item[:breach_duration] = @text.to_i
|
665
673
|
when 'UpperBreachScaleIncrement' then @item[:upper_breach_scale_increment] = @text.to_i
|
666
674
|
when 'UpperThreshold' then @item[:upper_threshold] = @text.to_f
|
File without changes
|
@@ -24,7 +24,6 @@
|
|
24
24
|
# Test
|
25
25
|
module RightAws
|
26
26
|
require 'digest/md5'
|
27
|
-
require 'pp'
|
28
27
|
|
29
28
|
class AwsUtils #:nodoc:
|
30
29
|
@@digest1 = OpenSSL::Digest::Digest.new("sha1")
|
@@ -32,6 +31,13 @@ module RightAws
|
|
32
31
|
if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000
|
33
32
|
@@digest256 = OpenSSL::Digest::Digest.new("sha256") rescue nil # Some installation may not support sha256
|
34
33
|
end
|
34
|
+
|
35
|
+
def self.utc_iso8601(time)
|
36
|
+
if time.is_a?(Fixnum) then time = Time::at(time)
|
37
|
+
elsif time.is_a?(String) then time = Time::parse(time)
|
38
|
+
end
|
39
|
+
time.utc.strftime("%Y-%m-%dT%H:%M:%S.000Z")
|
40
|
+
end
|
35
41
|
|
36
42
|
def self.sign(aws_secret_access_key, auth_string)
|
37
43
|
Base64.encode64(OpenSSL::HMAC.digest(@@digest1, aws_secret_access_key, auth_string)).strip
|
@@ -45,9 +51,17 @@ module RightAws
|
|
45
51
|
end
|
46
52
|
end
|
47
53
|
|
54
|
+
def self.xml_escape(text) # :nodoc:
|
55
|
+
REXML::Text::normalize(text)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.xml_unescape(text) # :nodoc:
|
59
|
+
REXML::Text::unnormalize(text)
|
60
|
+
end
|
61
|
+
|
48
62
|
# Set a timestamp and a signature version
|
49
63
|
def self.fix_service_params(service_hash, signature)
|
50
|
-
service_hash["Timestamp"] ||= Time.now
|
64
|
+
service_hash["Timestamp"] ||= utc_iso8601(Time.now) unless service_hash["Expires"]
|
51
65
|
service_hash["SignatureVersion"] = signature
|
52
66
|
service_hash
|
53
67
|
end
|
@@ -124,10 +138,24 @@ module RightAws
|
|
124
138
|
end
|
125
139
|
|
126
140
|
def self.split_items_and_params(array)
|
127
|
-
items = array.
|
141
|
+
items = Array(array).flatten.compact
|
128
142
|
params = items.last.kind_of?(Hash) ? items.pop : {}
|
129
143
|
[items, params]
|
130
144
|
end
|
145
|
+
|
146
|
+
# Generates a token in format of:
|
147
|
+
# 1. "1dd8d4e4-db6b-11df-b31d-0025b37efad0 (if UUID gem is loaded)
|
148
|
+
# 2. "1287483761-855215-zSv2z-bWGj2-31M5t-ags9m" (if UUID gem is not loaded)
|
149
|
+
TOKEN_GENERATOR_CHARSET = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
|
150
|
+
def self.generate_unique_token
|
151
|
+
time = Time.now
|
152
|
+
token = "%d-%06d" % [time.to_i, time.usec]
|
153
|
+
4.times do
|
154
|
+
token << "-"
|
155
|
+
5.times { token << TOKEN_GENERATOR_CHARSET[rand(TOKEN_GENERATOR_CHARSET.size)] }
|
156
|
+
end
|
157
|
+
token
|
158
|
+
end
|
131
159
|
end
|
132
160
|
|
133
161
|
class AwsBenchmarkingBlock #:nodoc:
|
@@ -173,7 +201,31 @@ module RightAws
|
|
173
201
|
def self.amazon_problems=(problems_list)
|
174
202
|
@@amazon_problems = problems_list
|
175
203
|
end
|
176
|
-
|
204
|
+
|
205
|
+
# Raise an exception if a timeout occures while an API call is in progress.
|
206
|
+
# This helps to avoid a duplicate resources creation when Amazon hangs for some time and
|
207
|
+
# RightHttpConnection is forced to use retries to get a response from it.
|
208
|
+
#
|
209
|
+
# If an API call action is in the list then no attempts to retry are performed.
|
210
|
+
#
|
211
|
+
RAISE_ON_TIMEOUT_ON_ACTIONS = %w{
|
212
|
+
AllocateAddress
|
213
|
+
CreateSnapshot
|
214
|
+
CreateVolume
|
215
|
+
PurchaseReservedInstancesOffering
|
216
|
+
RequestSpotInstances
|
217
|
+
RunInstances
|
218
|
+
}
|
219
|
+
@@raise_on_timeout_on_actions = RAISE_ON_TIMEOUT_ON_ACTIONS.dup
|
220
|
+
|
221
|
+
def self.raise_on_timeout_on_actions
|
222
|
+
@@raise_on_timeout_on_actions
|
223
|
+
end
|
224
|
+
|
225
|
+
def self.raise_on_timeout_on_actions=(actions_list)
|
226
|
+
@@raise_on_timeout_on_actions = actions_list
|
227
|
+
end
|
228
|
+
|
177
229
|
end
|
178
230
|
|
179
231
|
module RightAwsBaseInterface
|
@@ -214,33 +266,42 @@ module RightAws
|
|
214
266
|
@params = params
|
215
267
|
# If one defines EC2_URL he may forget to use a single slash as an "empty service" path.
|
216
268
|
# Amazon does not like this therefore add this bad boy if he is missing...
|
217
|
-
service_info[:default_service] = '/' if service_info[:default_service].
|
269
|
+
service_info[:default_service] = '/' if service_info[:default_service].right_blank?
|
218
270
|
raise AwsError.new("AWS access keys are required to operate on #{service_info[:name]}") \
|
219
|
-
if aws_access_key_id.
|
271
|
+
if aws_access_key_id.right_blank? || aws_secret_access_key.right_blank?
|
220
272
|
@aws_access_key_id = aws_access_key_id
|
221
273
|
@aws_secret_access_key = aws_secret_access_key
|
222
274
|
# if the endpoint was explicitly defined - then use it
|
223
275
|
if @params[:endpoint_url]
|
224
|
-
|
225
|
-
@params[:
|
226
|
-
@params[:
|
276
|
+
uri = URI.parse(@params[:endpoint_url])
|
277
|
+
@params[:server] = uri.host
|
278
|
+
@params[:port] = uri.port
|
279
|
+
@params[:service] = uri.path
|
280
|
+
@params[:protocol] = uri.scheme
|
227
281
|
# make sure the 'service' path is not empty
|
228
|
-
@params[:service] = service_info[:default_service] if @params[:service].
|
229
|
-
@params[:protocol] = URI.parse(@params[:endpoint_url]).scheme
|
282
|
+
@params[:service] = service_info[:default_service] if @params[:service].right_blank?
|
230
283
|
@params[:region] = nil
|
284
|
+
default_port = uri.default_port
|
231
285
|
else
|
232
286
|
@params[:server] ||= service_info[:default_host]
|
233
287
|
@params[:server] = "#{@params[:region]}.#{@params[:server]}" if @params[:region]
|
234
288
|
@params[:port] ||= service_info[:default_port]
|
235
289
|
@params[:service] ||= service_info[:default_service]
|
236
290
|
@params[:protocol] ||= service_info[:default_protocol]
|
291
|
+
default_port = @params[:protocol] == 'https' ? 443 : 80
|
237
292
|
end
|
238
|
-
#
|
293
|
+
# build a host name to sign
|
294
|
+
@params[:host_to_sign] = @params[:server].dup
|
295
|
+
@params[:host_to_sign] << ":#{@params[:port]}" unless default_port == @params[:port].to_i
|
296
|
+
# a set of options to be passed to RightHttpConnection object
|
297
|
+
@params[:connection_options] = {} unless @params[:connection_options].is_a?(Hash)
|
298
|
+
@with_connection_options = {}
|
239
299
|
@params[:connections] ||= :shared # || :dedicated
|
240
300
|
@params[:max_connections] ||= 10
|
241
301
|
@params[:connection_lifetime] ||= 20*60
|
242
302
|
@params[:api_version] ||= service_info[:default_api_version]
|
243
303
|
@logger = @params[:logger]
|
304
|
+
@logger = ::Rails.logger if !@logger && defined?(::Rails) && ::Rails.respond_to?(:logger)
|
244
305
|
@logger = RAILS_DEFAULT_LOGGER if !@logger && defined?(RAILS_DEFAULT_LOGGER)
|
245
306
|
@logger = Logger.new(STDOUT) if !@logger
|
246
307
|
@logger.info "New #{self.class.name} using #{@params[:connections]} connections mode"
|
@@ -275,7 +336,8 @@ module RightAws
|
|
275
336
|
# get rid of requestId (this bad boy was added for API 2008-08-08+ and it is uniq for every response)
|
276
337
|
# feb 04, 2009 (load balancer uses 'RequestId' hence use 'i' modifier to hit it also)
|
277
338
|
response = response.sub(%r{<requestId>.+?</requestId>}i, '')
|
278
|
-
|
339
|
+
# this should work for both ruby 1.8.x and 1.9.x
|
340
|
+
response_md5 = Digest::MD5::new.update(response).to_s
|
279
341
|
# check for changes
|
280
342
|
unless @cache[function] && @cache[function][:response_md5] == response_md5
|
281
343
|
# well, the response is new, reset cache data
|
@@ -306,11 +368,57 @@ module RightAws
|
|
306
368
|
raise if $!.is_a?(AwsNoChange)
|
307
369
|
AwsError::on_aws_exception(self, options)
|
308
370
|
end
|
309
|
-
|
310
|
-
|
311
|
-
#
|
312
|
-
|
313
|
-
|
371
|
+
|
372
|
+
#----------------------------
|
373
|
+
# HTTP Connections handling
|
374
|
+
#----------------------------
|
375
|
+
|
376
|
+
def get_server_url(request) # :nodoc:
|
377
|
+
"#{request[:protocol]}://#{request[:server]}:#{request[:port]}"
|
378
|
+
end
|
379
|
+
|
380
|
+
def get_connections_storage(aws_service) # :nodoc:
|
381
|
+
case @params[:connections].to_s
|
382
|
+
when 'dedicated' then @connections_storage ||= {}
|
383
|
+
else Thread.current[aws_service] ||= {}
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
def destroy_connection(request, reason) # :nodoc:
|
388
|
+
connections = get_connections_storage(request[:aws_service])
|
389
|
+
server_url = get_server_url(request)
|
390
|
+
if connections[server_url]
|
391
|
+
connections[server_url][:connection].finish(reason)
|
392
|
+
connections.delete(server_url)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
# Expire the connection if it has expired.
|
397
|
+
def get_connection(request) # :nodoc:
|
398
|
+
server_url = get_server_url(request)
|
399
|
+
connection_storage = get_connections_storage(request[:aws_service])
|
400
|
+
life_time_scratch = Time.now-@params[:connection_lifetime]
|
401
|
+
# Delete out-of-dated connections
|
402
|
+
connections_in_list = 0
|
403
|
+
connection_storage.to_a.sort{|conn1, conn2| conn2[1][:last_used_at] <=> conn1[1][:last_used_at]}.each do |serv_url, conn_opts|
|
404
|
+
if @params[:max_connections] <= connections_in_list
|
405
|
+
conn_opts[:connection].finish('out-of-limit')
|
406
|
+
connection_storage.delete(server_url)
|
407
|
+
elsif conn_opts[:last_used_at] < life_time_scratch
|
408
|
+
conn_opts[:connection].finish('out-of-date')
|
409
|
+
connection_storage.delete(server_url)
|
410
|
+
else
|
411
|
+
connections_in_list += 1
|
412
|
+
end
|
413
|
+
end
|
414
|
+
connection = (connection_storage[server_url] ||= {})
|
415
|
+
connection[:last_used_at] = Time.now
|
416
|
+
connection[:connection] ||= Rightscale::HttpConnection.new(:exception => RightAws::AwsError, :logger => @logger)
|
417
|
+
end
|
418
|
+
|
419
|
+
#----------------------------
|
420
|
+
# HTTP Requests handling
|
421
|
+
#----------------------------
|
314
422
|
|
315
423
|
# ACF, AMS, EC2, LBS and SDB uses this guy
|
316
424
|
# SQS and S3 use their own methods
|
@@ -325,13 +433,13 @@ module RightAws
|
|
325
433
|
"Version" => @params[:api_version] }
|
326
434
|
service_hash.merge!(options)
|
327
435
|
# Sign request options
|
328
|
-
service_params = signed_service_params(@aws_secret_access_key, service_hash, http_verb, @params[:
|
436
|
+
service_params = signed_service_params(@aws_secret_access_key, service_hash, http_verb, @params[:host_to_sign], @params[:service])
|
329
437
|
# Use POST if the length of the query string is too large
|
330
438
|
# see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/MakingRESTRequests.html
|
331
439
|
if http_verb != 'POST' && service_params.size > 2000
|
332
440
|
http_verb = 'POST'
|
333
441
|
if signature_version == '2'
|
334
|
-
service_params = signed_service_params(@aws_secret_access_key, service_hash, http_verb, @params[:
|
442
|
+
service_params = signed_service_params(@aws_secret_access_key, service_hash, http_verb, @params[:host_to_sign], @params[:service])
|
335
443
|
end
|
336
444
|
end
|
337
445
|
# create a request
|
@@ -341,48 +449,31 @@ module RightAws
|
|
341
449
|
when 'POST'
|
342
450
|
request = Net::HTTP::Post.new(@params[:service])
|
343
451
|
request.body = service_params
|
344
|
-
request['Content-Type'] = 'application/x-www-form-urlencoded'
|
452
|
+
request['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'
|
345
453
|
else
|
346
454
|
raise "Unsupported HTTP verb #{verb.inspect}!"
|
347
455
|
end
|
348
456
|
# prepare output hash
|
349
|
-
{ :request => request,
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
#
|
358
|
-
|
359
|
-
|
360
|
-
@connections_storage ||= {}
|
361
|
-
else # 'dedicated'
|
362
|
-
@connections_storage = (Thread.current[aws_service] ||= {})
|
363
|
-
end
|
364
|
-
#
|
365
|
-
@connections_storage[server_url] ||= {}
|
366
|
-
@connections_storage[server_url][:last_used_at] = Time.now
|
367
|
-
@connections_storage[server_url][:connection] ||= Rightscale::HttpConnection.new(:exception => RightAws::AwsError, :logger => @logger)
|
368
|
-
# keep X most recent connections (but were used not far than Y minutes ago)
|
369
|
-
connections = 0
|
370
|
-
@connections_storage.to_a.sort{|i1, i2| i2[1][:last_used_at] <=> i1[1][:last_used_at]}.to_a.each do |i|
|
371
|
-
if i[0] != server_url && (@params[:max_connections] <= connections || i[1][:last_used_at] < Time.now - @params[:connection_lifetime])
|
372
|
-
# delete the connection from the list
|
373
|
-
@connections_storage.delete(i[0])
|
374
|
-
# then finish it
|
375
|
-
i[1][:connection].finish((@params[:max_connections] <= connections) ? "out-of-limit" : "out-of-date") rescue nil
|
376
|
-
else
|
377
|
-
connections += 1
|
378
|
-
end
|
457
|
+
request_hash = { :request => request,
|
458
|
+
:server => @params[:server],
|
459
|
+
:port => @params[:port],
|
460
|
+
:protocol => @params[:protocol] }
|
461
|
+
request_hash.merge!(@params[:connection_options])
|
462
|
+
request_hash.merge!(@with_connection_options)
|
463
|
+
|
464
|
+
# If an action is marked as "non-retryable" and there was no :raise_on_timeout option set
|
465
|
+
# explicitly then do set that option
|
466
|
+
if Array(RightAwsBase::raise_on_timeout_on_actions).include?(action) && !request_hash.has_key?(:raise_on_timeout)
|
467
|
+
request_hash.merge!(:raise_on_timeout => true)
|
379
468
|
end
|
380
|
-
|
469
|
+
|
470
|
+
request_hash
|
381
471
|
end
|
382
472
|
|
383
473
|
# All services uses this guy.
|
384
474
|
def request_info_impl(aws_service, benchblock, request, parser, &block) #:nodoc:
|
385
|
-
|
475
|
+
request[:aws_service] = aws_service
|
476
|
+
@connection = get_connection(request)
|
386
477
|
@last_request = request[:request]
|
387
478
|
@last_response = nil
|
388
479
|
response = nil
|
@@ -397,25 +488,31 @@ module RightAws
|
|
397
488
|
# Exceptions can originate from code directly in the block, or from user
|
398
489
|
# code called in the other block which is passed to response.read_body.
|
399
490
|
benchblock.service.add! do
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
response.read_body(&block)
|
407
|
-
else
|
408
|
-
@error_handler = AWSErrorHandler.new(self, parser, :errors_list => self.class.amazon_problems) unless @error_handler
|
409
|
-
check_result = @error_handler.check(request)
|
410
|
-
if check_result
|
491
|
+
begin
|
492
|
+
responsehdr = @connection.request(request) do |response|
|
493
|
+
#########
|
494
|
+
begin
|
495
|
+
@last_response = response
|
496
|
+
if response.is_a?(Net::HTTPSuccess)
|
411
497
|
@error_handler = nil
|
412
|
-
|
498
|
+
response.read_body(&block)
|
499
|
+
else
|
500
|
+
@error_handler = AWSErrorHandler.new(self, parser, :errors_list => self.class.amazon_problems) unless @error_handler
|
501
|
+
check_result = @error_handler.check(request)
|
502
|
+
if check_result
|
503
|
+
@error_handler = nil
|
504
|
+
return check_result
|
505
|
+
end
|
506
|
+
raise AwsError.new(@last_errors, @last_response.code, @last_request_id)
|
413
507
|
end
|
414
|
-
|
508
|
+
rescue Exception => e
|
509
|
+
blockexception = e
|
415
510
|
end
|
416
|
-
rescue Exception => e
|
417
|
-
blockexception = e
|
418
511
|
end
|
512
|
+
rescue Exception => e
|
513
|
+
# Kill a connection if we run into a low level connection error
|
514
|
+
destroy_connection(request, "error: #{e.message}")
|
515
|
+
raise e
|
419
516
|
end
|
420
517
|
#########
|
421
518
|
|
@@ -429,7 +526,15 @@ module RightAws
|
|
429
526
|
return parser.result
|
430
527
|
end
|
431
528
|
else
|
432
|
-
benchblock.service.add!
|
529
|
+
benchblock.service.add! do
|
530
|
+
begin
|
531
|
+
response = @connection.request(request)
|
532
|
+
rescue Exception => e
|
533
|
+
# Kill a connection if we run into a low level connection error
|
534
|
+
destroy_connection(request, "error: #{e.message}")
|
535
|
+
raise e
|
536
|
+
end
|
537
|
+
end
|
433
538
|
# check response for errors...
|
434
539
|
@last_response = response
|
435
540
|
if response.is_a?(Net::HTTPSuccess)
|
@@ -451,7 +556,7 @@ module RightAws
|
|
451
556
|
raise
|
452
557
|
end
|
453
558
|
|
454
|
-
def request_cache_or_info(method, link, parser_class, benchblock, use_cache=true) #:nodoc:
|
559
|
+
def request_cache_or_info(method, link, parser_class, benchblock, use_cache=true, &block) #:nodoc:
|
455
560
|
# We do not want to break the logic of parsing hence will use a dummy parser to process all the standard
|
456
561
|
# steps (errors checking etc). The dummy parser does nothig - just returns back the params it received.
|
457
562
|
# If the caching is enabled and hit then throw AwsNoChange.
|
@@ -461,7 +566,7 @@ module RightAws
|
|
461
566
|
cache_hits?(method.to_sym, response.body) if use_cache
|
462
567
|
parser = parser_class.new(:logger => @logger)
|
463
568
|
benchblock.xml.add!{ parser.parse(response, params) }
|
464
|
-
result =
|
569
|
+
result = block ? block.call(parser) : parser.result
|
465
570
|
# update parsed data
|
466
571
|
update_cache(method.to_sym, :parsed => result) if use_cache
|
467
572
|
result
|
@@ -472,7 +577,24 @@ module RightAws
|
|
472
577
|
@last_response && @last_response.body.to_s[%r{<requestId>(.+?)</requestId>}i] && $1
|
473
578
|
end
|
474
579
|
|
580
|
+
# Incrementally lists something.
|
581
|
+
def incrementally_list_items(action, parser_class, params={}, &block) # :nodoc:
|
582
|
+
params = params.dup
|
583
|
+
params['MaxItems'] = params.delete(:max_items) if params[:max_items]
|
584
|
+
params['Marker'] = params.delete(:marker) if params[:marker]
|
585
|
+
last_response = nil
|
586
|
+
loop do
|
587
|
+
last_response = request_info( generate_request(action, params), parser_class.new(:logger => @logger))
|
588
|
+
params['Marker'] = last_response[:marker]
|
589
|
+
break unless block && block.call(last_response) && !last_response[:marker].right_blank?
|
590
|
+
end
|
591
|
+
last_response
|
592
|
+
end
|
593
|
+
|
475
594
|
# Format array of items into Amazons handy hash ('?' is a place holder):
|
595
|
+
# Options:
|
596
|
+
# :default => "something" : Set a value to "something" when it is nil
|
597
|
+
# :default => :skip_nils : Skip nil values
|
476
598
|
#
|
477
599
|
# amazonize_list('Item', ['a', 'b', 'c']) =>
|
478
600
|
# { 'Item.1' => 'a', 'Item.2' => 'b', 'Item.3' => 'c' }
|
@@ -496,22 +618,114 @@ module RightAws
|
|
496
618
|
# "Filter.2.Key"=>"B",
|
497
619
|
# "Filter.2.Value.1"=>"ba",
|
498
620
|
# "Filter.2.Value.2"=>"bb"}
|
499
|
-
def amazonize_list(masks, list) #:nodoc:
|
621
|
+
def amazonize_list(masks, list, options={}) #:nodoc:
|
500
622
|
groups = {}
|
501
|
-
list.
|
502
|
-
masks.
|
623
|
+
Array(list).each_with_index do |list_item, i|
|
624
|
+
Array(masks).each_with_index do |mask, mask_idx|
|
503
625
|
key = mask[/\?/] ? mask.dup : mask.dup + '.?'
|
504
626
|
key.sub!('?', (i+1).to_s)
|
505
|
-
value = list_item
|
627
|
+
value = Array(list_item)[mask_idx]
|
506
628
|
if value.is_a?(Array)
|
507
|
-
groups.merge!(amazonize_list(key, value))
|
629
|
+
groups.merge!(amazonize_list(key, value, options))
|
508
630
|
else
|
631
|
+
if value.nil?
|
632
|
+
next if options[:default] == :skip_nils
|
633
|
+
value = options[:default]
|
634
|
+
end
|
635
|
+
# Hack to avoid having unhandled '?' in keys : do replace them all with '1':
|
636
|
+
# bad: ec2.amazonize_list(['Filter.?.Key', 'Filter.?.Value.?'], { a: => :b }) => {"Filter.1.Key"=>:a, "Filter.1.Value.?"=>1}
|
637
|
+
# good: ec2.amazonize_list(['Filter.?.Key', 'Filter.?.Value.?'], { a: => :b }) => {"Filter.1.Key"=>:a, "Filter.1.Value.1"=>1}
|
638
|
+
key.gsub!('?', '1')
|
509
639
|
groups[key] = value
|
510
640
|
end
|
511
641
|
end
|
512
642
|
end
|
513
643
|
groups
|
514
644
|
end
|
645
|
+
|
646
|
+
BLOCK_DEVICE_KEY_MAPPING = { # :nodoc:
|
647
|
+
:device_name => 'DeviceName',
|
648
|
+
:virtual_name => 'VirtualName',
|
649
|
+
:no_device => 'NoDevice',
|
650
|
+
:ebs_snapshot_id => 'Ebs.SnapshotId',
|
651
|
+
:ebs_volume_size => 'Ebs.VolumeSize',
|
652
|
+
:ebs_delete_on_termination => 'Ebs.DeleteOnTermination' }
|
653
|
+
|
654
|
+
def amazonize_block_device_mappings(block_device_mappings, key = 'BlockDeviceMapping') # :nodoc:
|
655
|
+
result = {}
|
656
|
+
unless block_device_mappings.right_blank?
|
657
|
+
block_device_mappings = [block_device_mappings] unless block_device_mappings.is_a?(Array)
|
658
|
+
block_device_mappings.each_with_index do |b, idx|
|
659
|
+
BLOCK_DEVICE_KEY_MAPPING.each do |local_name, remote_name|
|
660
|
+
value = b[local_name]
|
661
|
+
case local_name
|
662
|
+
when :no_device then value = value ? '' : nil # allow to pass :no_device as boolean
|
663
|
+
end
|
664
|
+
result["#{key}.#{idx+1}.#{remote_name}"] = value unless value.nil?
|
665
|
+
end
|
666
|
+
end
|
667
|
+
end
|
668
|
+
result
|
669
|
+
end
|
670
|
+
|
671
|
+
# Execute a block of code with custom set of settings for right_http_connection.
|
672
|
+
# Accepts next options (see Rightscale::HttpConnection for explanation):
|
673
|
+
# :raise_on_timeout
|
674
|
+
# :http_connection_retry_count
|
675
|
+
# :http_connection_open_timeout
|
676
|
+
# :http_connection_read_timeout
|
677
|
+
# :http_connection_retry_delay
|
678
|
+
# :user_agent
|
679
|
+
# :exception
|
680
|
+
#
|
681
|
+
# Example #1:
|
682
|
+
#
|
683
|
+
# # Try to create a snapshot but stop with exception if timeout is received
|
684
|
+
# # to avoid having a duplicate API calls that create duplicate snapshots.
|
685
|
+
# ec2 = Rightscale::Ec2::new(aws_access_key_id, aws_secret_access_key)
|
686
|
+
# ec2.with_connection_options(:raise_on_timeout => true) do
|
687
|
+
# ec2.create_snapshot('vol-898a6fe0', 'KD: WooHoo!!')
|
688
|
+
# end
|
689
|
+
#
|
690
|
+
# Example #2:
|
691
|
+
#
|
692
|
+
# # Opposite case when the setting is global:
|
693
|
+
# @ec2 = Rightscale::Ec2::new(aws_access_key_id, aws_secret_access_key,
|
694
|
+
# :connection_options => { :raise_on_timeout => true })
|
695
|
+
# # Create an SSHKey but do tries on timeout
|
696
|
+
# ec2.with_connection_options(:raise_on_timeout => false) do
|
697
|
+
# new_key = ec2.create_key_pair('my_test_key')
|
698
|
+
# end
|
699
|
+
#
|
700
|
+
# Example #3:
|
701
|
+
#
|
702
|
+
# # Global settings (HttpConnection level):
|
703
|
+
# Rightscale::HttpConnection::params[:http_connection_open_timeout] = 5
|
704
|
+
# Rightscale::HttpConnection::params[:http_connection_read_timeout] = 250
|
705
|
+
# Rightscale::HttpConnection::params[:http_connection_retry_count] = 2
|
706
|
+
#
|
707
|
+
# # Local setings (RightAws level)
|
708
|
+
# ec2 = Rightscale::Ec2::new(AWS_ID, AWS_KEY,
|
709
|
+
# :region => 'us-east-1',
|
710
|
+
# :connection_options => {
|
711
|
+
# :http_connection_read_timeout => 2,
|
712
|
+
# :http_connection_retry_count => 5,
|
713
|
+
# :user_agent => 'Mozilla 4.0'
|
714
|
+
# })
|
715
|
+
#
|
716
|
+
# # Custom settings (API call level)
|
717
|
+
# ec2.with_connection_options(:raise_on_timeout => true,
|
718
|
+
# :http_connection_read_timeout => 10,
|
719
|
+
# :user_agent => '') do
|
720
|
+
# pp ec2.describe_images
|
721
|
+
# end
|
722
|
+
#
|
723
|
+
def with_connection_options(options, &block)
|
724
|
+
@with_connection_options = options
|
725
|
+
block.call self
|
726
|
+
ensure
|
727
|
+
@with_connection_options = {}
|
728
|
+
end
|
515
729
|
end
|
516
730
|
|
517
731
|
|
@@ -632,7 +846,7 @@ module RightAws
|
|
632
846
|
@reiteration_delay = @@reiteration_start_delay
|
633
847
|
@retries = 0
|
634
848
|
# close current HTTP(S) connection on 5xx, errors from list and 4xx errors
|
635
|
-
@close_on_error = params[:close_on_error].nil? ? @@close_on_error : params[:close_on_error]
|
849
|
+
@close_on_error = params[:close_on_error].nil? ? @@close_on_error : params[:close_on_error]
|
636
850
|
@close_on_4xx_probability = params[:close_on_4xx_probability] || @@close_on_4xx_probability
|
637
851
|
end
|
638
852
|
|
@@ -675,10 +889,17 @@ module RightAws
|
|
675
889
|
# Ok, it is a redirect, find the new destination location
|
676
890
|
if redirect_detected
|
677
891
|
location = response['location']
|
892
|
+
# As for 301 ( Moved Permanently) Amazon does not return a 'Location' header but
|
893
|
+
# it is possible to extract a new endpoint from the response body
|
894
|
+
if location.right_blank? && response.code=='301' && response.body
|
895
|
+
new_endpoint = response.body[/<Endpoint>(.*?)<\/Endpoint>/] && $1
|
896
|
+
location = "#{request[:protocol]}://#{new_endpoint}:#{request[:port]}#{request[:request].path}"
|
897
|
+
end
|
678
898
|
# ... log information and ...
|
679
899
|
@aws.logger.info("##### #{@aws.class.name} redirect requested: #{response.code} #{response.message} #####")
|
680
900
|
@aws.logger.info(" Old location: #{request_text_data}")
|
681
901
|
@aws.logger.info(" New location: #{location}")
|
902
|
+
@aws.logger.info(" Request Verb: #{request[:request].class.name}")
|
682
903
|
# ... fix the connection data
|
683
904
|
request[:server] = URI.parse(location).host
|
684
905
|
request[:protocol] = URI.parse(location).scheme
|
@@ -701,7 +922,7 @@ module RightAws
|
|
701
922
|
# It may have a chance that one server is a semi-down and reconnection
|
702
923
|
# will help us to connect to the other server
|
703
924
|
if !redirect_detected && @close_on_error
|
704
|
-
@aws.
|
925
|
+
@aws.destroy_connection(request, "#{self.class.name}: error match to pattern '#{error_match}'")
|
705
926
|
end
|
706
927
|
|
707
928
|
if (Time.now < @stop_at)
|
@@ -730,13 +951,13 @@ module RightAws
|
|
730
951
|
end
|
731
952
|
# aha, this is unhandled error:
|
732
953
|
elsif @close_on_error
|
733
|
-
#
|
734
|
-
if @aws.last_response.code.to_s[/^5\d\d$/]
|
735
|
-
@aws.
|
954
|
+
# On 5xx(Server errors), 403(RequestTimeTooSkewed) and 408(Request Timeout) a conection has to be closed
|
955
|
+
if @aws.last_response.code.to_s[/^(5\d\d|403|408)$/]
|
956
|
+
@aws.destroy_connection(request, "#{self.class.name}: code: #{@aws.last_response.code}: '#{@aws.last_response.message}'")
|
736
957
|
# Is this a 4xx error ?
|
737
958
|
elsif @aws.last_response.code.to_s[/^4\d\d$/] && @close_on_4xx_probability > rand(100)
|
738
|
-
@aws.
|
739
|
-
|
959
|
+
@aws.destroy_connection(request, "#{self.class.name}: code: #{@aws.last_response.code}: '#{@aws.last_response.message}', " +
|
960
|
+
"probability: #{@close_on_4xx_probability}%")
|
740
961
|
end
|
741
962
|
end
|
742
963
|
result
|
@@ -747,29 +968,41 @@ module RightAws
|
|
747
968
|
|
748
969
|
#-----------------------------------------------------------------
|
749
970
|
|
750
|
-
class
|
751
|
-
def self.include_callback
|
752
|
-
include XML::SaxParser::Callbacks
|
753
|
-
end
|
971
|
+
class RightSaxParserCallbackTemplate #:nodoc:
|
754
972
|
def initialize(right_aws_parser)
|
755
973
|
@right_aws_parser = right_aws_parser
|
756
974
|
end
|
757
|
-
def on_start_element(name, attr_hash)
|
758
|
-
@right_aws_parser.tag_start(name, attr_hash)
|
759
|
-
end
|
760
975
|
def on_characters(chars)
|
761
976
|
@right_aws_parser.text(chars)
|
762
977
|
end
|
763
|
-
def on_end_element(name)
|
764
|
-
@right_aws_parser.tag_end(name)
|
765
|
-
end
|
766
978
|
def on_start_document; end
|
767
979
|
def on_comment(msg); end
|
768
980
|
def on_processing_instruction(target, data); end
|
769
981
|
def on_cdata_block(cdata); end
|
770
982
|
def on_end_document; end
|
771
983
|
end
|
772
|
-
|
984
|
+
|
985
|
+
class RightSaxParserCallback < RightSaxParserCallbackTemplate
|
986
|
+
def self.include_callback
|
987
|
+
include XML::SaxParser::Callbacks
|
988
|
+
end
|
989
|
+
def on_start_element(name, attr_hash)
|
990
|
+
@right_aws_parser.tag_start(name, attr_hash)
|
991
|
+
end
|
992
|
+
def on_end_element(name)
|
993
|
+
@right_aws_parser.tag_end(name)
|
994
|
+
end
|
995
|
+
end
|
996
|
+
|
997
|
+
class RightSaxParserCallbackNs < RightSaxParserCallbackTemplate
|
998
|
+
def on_start_element_ns(name, attr_hash, prefix, uri, namespaces)
|
999
|
+
@right_aws_parser.tag_start(name, attr_hash)
|
1000
|
+
end
|
1001
|
+
def on_end_element_ns(name, prefix, uri)
|
1002
|
+
@right_aws_parser.tag_end(name)
|
1003
|
+
end
|
1004
|
+
end
|
1005
|
+
|
773
1006
|
class RightAWSParser #:nodoc:
|
774
1007
|
# default parsing library
|
775
1008
|
DEFAULT_XML_LIBRARY = 'rexml'
|
@@ -830,27 +1063,35 @@ module RightAws
|
|
830
1063
|
if @xml_lib=='libxml' && !defined?(XML::SaxParser)
|
831
1064
|
begin
|
832
1065
|
require 'xml/libxml'
|
833
|
-
#
|
834
|
-
if XML::Parser::VERSION >= '0.5.1
|
835
|
-
|
1066
|
+
# Setup SaxParserCallback
|
1067
|
+
if XML::Parser::VERSION >= '0.5.1' &&
|
1068
|
+
XML::Parser::VERSION < '0.9.7'
|
1069
|
+
RightSaxParserCallback.include_callback
|
836
1070
|
end
|
837
1071
|
rescue LoadError => e
|
838
|
-
@@supported_xml_libs.delete(@xml_lib)
|
839
|
-
@xml_lib = DEFAULT_XML_LIBRARY
|
1072
|
+
@@supported_xml_libs.delete(@xml_lib)
|
1073
|
+
@xml_lib = DEFAULT_XML_LIBRARY
|
840
1074
|
if @logger
|
841
1075
|
@logger.error e.inspect
|
842
1076
|
@logger.error e.backtrace
|
843
|
-
@logger.info "Can not load 'libxml' library. '#{DEFAULT_XML_LIBRARY}' is used for parsing."
|
1077
|
+
@logger.info "Can not load 'libxml' library. '#{DEFAULT_XML_LIBRARY}' is used for parsing."
|
844
1078
|
end
|
845
1079
|
end
|
846
1080
|
end
|
847
1081
|
# Parse the xml text
|
848
1082
|
case @xml_lib
|
849
|
-
when 'libxml'
|
850
|
-
|
851
|
-
|
1083
|
+
when 'libxml'
|
1084
|
+
if XML::Parser::VERSION >= '0.9.9'
|
1085
|
+
# avoid warning on every usage
|
1086
|
+
xml = XML::SaxParser.string(xml_text)
|
1087
|
+
else
|
1088
|
+
xml = XML::SaxParser.new
|
1089
|
+
xml.string = xml_text
|
1090
|
+
end
|
852
1091
|
# check libxml-ruby version
|
853
|
-
if
|
1092
|
+
if XML::Parser::VERSION >= '0.9.7'
|
1093
|
+
xml.callbacks = RightSaxParserCallbackNs.new(self)
|
1094
|
+
elsif XML::Parser::VERSION >= '0.5.1'
|
854
1095
|
xml.callbacks = RightSaxParserCallback.new(self)
|
855
1096
|
else
|
856
1097
|
xml.on_start_element{|name, attr_hash| self.tag_start(name, attr_hash)}
|
@@ -929,5 +1170,11 @@ module RightAws
|
|
929
1170
|
end
|
930
1171
|
end
|
931
1172
|
|
1173
|
+
class RightBoolResponseParser < RightAWSParser #:nodoc:
|
1174
|
+
def tagend(name)
|
1175
|
+
@result = (@text=='true') if name == 'return'
|
1176
|
+
end
|
1177
|
+
end
|
1178
|
+
|
932
1179
|
end
|
933
1180
|
|