awsum 0.5 → 0.5.1

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.
Files changed (43) hide show
  1. data/Gemfile +1 -1
  2. data/Gemfile.lock +14 -20
  3. data/README.rdoc +1 -1
  4. data/Rakefile +3 -3
  5. data/functional/ec2/instances_spec.rb +46 -0
  6. data/functional/spec_helper.rb +71 -0
  7. data/lib/awsum.rb +2 -2
  8. data/lib/awsum/ec2.rb +85 -68
  9. data/lib/awsum/ec2/image.rb +1 -1
  10. data/lib/awsum/ec2/instance.rb +35 -6
  11. data/lib/awsum/ec2/parsers/image_parser.rb +3 -5
  12. data/lib/awsum/ec2/parsers/instance_parser.rb +24 -15
  13. data/lib/awsum/ec2/parsers/snapshot_parser.rb +3 -3
  14. data/lib/awsum/ec2/parsers/tag_parser.rb +56 -0
  15. data/lib/awsum/ec2/parsers/volume_parser.rb +3 -5
  16. data/lib/awsum/ec2/region.rb +8 -5
  17. data/lib/awsum/ec2/security_group.rb +12 -16
  18. data/lib/awsum/ec2/snapshot.rb +9 -0
  19. data/lib/awsum/ec2/state.rb +22 -0
  20. data/lib/awsum/ec2/tag.rb +17 -0
  21. data/lib/awsum/ec2/volume.rb +12 -2
  22. data/lib/awsum/requestable.rb +53 -3
  23. data/spec/fixtures/ec2/authorize_group_access.xml +12 -0
  24. data/spec/fixtures/ec2/create_security_group.xml +3 -3
  25. data/spec/fixtures/ec2/delete_security_group.xml +3 -3
  26. data/spec/fixtures/ec2/purchase_reserved_instances_offering2.xml +5 -0
  27. data/spec/fixtures/ec2/tags.xml +12 -0
  28. data/spec/lib/awsum/ec2/image_spec.rb +60 -0
  29. data/spec/lib/awsum/ec2/instance_spec.rb +39 -1
  30. data/spec/lib/awsum/ec2/parsers/instance_parser_spec.rb +4 -3
  31. data/spec/lib/awsum/ec2/parsers/tag_parser_spec.rb +29 -0
  32. data/spec/lib/awsum/ec2/region_spec.rb +31 -2
  33. data/spec/lib/awsum/ec2/reserved_instance_spec.rb +3 -19
  34. data/spec/lib/awsum/ec2/security_group_spec.rb +123 -51
  35. data/spec/lib/awsum/ec2/snapshots_spec.rb +27 -1
  36. data/spec/lib/awsum/ec2/state_spec.rb +11 -0
  37. data/spec/lib/awsum/ec2/tag_spec.rb +45 -0
  38. data/spec/lib/awsum/ec2/volume_spec.rb +47 -0
  39. data/spec/lib/awsum/requestable_spec.rb +1 -1
  40. data/tools/dump.rb +55 -0
  41. metadata +176 -32
  42. data/.gitignore +0 -5
  43. data/spec/fixtures/ec2/purchase_reserved_instances_offerings.xml +0 -6
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ gem "bundler", "~> 1.0.0"
5
5
 
6
6
  group :development do
7
7
  gem "jeweler"
8
- gem "rspec", ">= 2.0.0.beta.22"
8
+ gem "rspec", ">= 2.0.0"
9
9
  gem "fakeweb"
10
10
 
11
11
  gem "shoulda"
@@ -2,30 +2,24 @@ GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
4
  diff-lcs (1.1.2)
5
- fakeweb (1.2.8)
6
- gemcutter (0.6.1)
5
+ fakeweb (1.3.0)
7
6
  git (1.2.5)
8
- jeweler (1.4.0)
9
- gemcutter (>= 0.1.0)
7
+ jeweler (1.5.2)
8
+ bundler (~> 1.0.0)
10
9
  git (>= 1.2.5)
11
- rubyforge (>= 2.0.0)
12
- json_pure (1.4.6)
13
- mocha (0.9.8)
10
+ rake
11
+ mocha (0.9.10)
14
12
  rake
15
13
  rake (0.8.7)
16
14
  rcov (0.9.9)
17
- rspec (2.0.0.beta.22)
18
- rspec-core (= 2.0.0.beta.22)
19
- rspec-expectations (= 2.0.0.beta.22)
20
- rspec-mocks (= 2.0.0.beta.22)
21
- rspec-core (2.0.0.beta.22)
22
- rspec-expectations (2.0.0.beta.22)
23
- diff-lcs (>= 1.1.2)
24
- rspec-mocks (2.0.0.beta.22)
25
- rspec-core (= 2.0.0.beta.22)
26
- rspec-expectations (= 2.0.0.beta.22)
27
- rubyforge (2.0.4)
28
- json_pure (>= 1.1.7)
15
+ rspec (2.4.0)
16
+ rspec-core (~> 2.4.0)
17
+ rspec-expectations (~> 2.4.0)
18
+ rspec-mocks (~> 2.4.0)
19
+ rspec-core (2.4.0)
20
+ rspec-expectations (2.4.0)
21
+ diff-lcs (~> 1.1.2)
22
+ rspec-mocks (2.4.0)
29
23
  shoulda (2.11.3)
30
24
  timecop (0.3.5)
31
25
 
@@ -39,6 +33,6 @@ DEPENDENCIES
39
33
  mocha
40
34
  rake
41
35
  rcov
42
- rspec (>= 2.0.0.beta.22)
36
+ rspec (>= 2.0.0)
43
37
  shoulda
44
38
  timecop
@@ -16,7 +16,7 @@ allowing you to work with objects in a very natural way.
16
16
 
17
17
  ==Working with different Regions
18
18
  You can use blocks to wrap your calls for a specific Region
19
- ec2.region('eu-west-1').use do
19
+ ec2.region('eu-west-1') do
20
20
  #Run an instance in the eu-west-1 region
21
21
  run_instance(...)
22
22
  end
data/Rakefile CHANGED
@@ -43,7 +43,7 @@ end
43
43
  desc 'Start an IRB session with all necessary files required.'
44
44
  task :shell do |t|
45
45
  chdir File.dirname(__FILE__)
46
- exec 'irb -I lib/ -I lib/awsum -r rubygems -r awsum'
46
+ exec 'irb -I lib/ -r rubygems -r awsum/ec2 -r awsum/s3'
47
47
  end
48
48
 
49
49
  desc 'Generate documentation.'
@@ -64,12 +64,12 @@ end
64
64
 
65
65
  require 'rspec/core/rake_task'
66
66
  RSpec::Core::RakeTask.new do |t|
67
- t.pattern = "./spec/lib/**/*_spec.rb"
67
+ t.pattern = "./spec/**/*_spec.rb"
68
68
  end
69
69
 
70
70
  namespace :spec do
71
71
  desc "Run RSpec integration code examples (LIVE runs against Amazon AWS)"
72
72
  RSpec::Core::RakeTask.new(:integration) do |t|
73
- t.pattern = "./spec/functional/**/*_spec.rb"
73
+ t.pattern = "./functional/**/*_spec.rb"
74
74
  end
75
75
  end
@@ -0,0 +1,46 @@
1
+ require File.join(File.dirname(__FILE__), '../spec_helper')
2
+
3
+ functional "Instances" do
4
+ let(:ec2) { Awsum::Ec2.new(access_key, secret_key) }
5
+
6
+ it "should succeed" do
7
+ image = run "retrieving an image" do
8
+ images = ec2.images(:filter => {:architecture => 'i386', :name => '*ubuntu*', 'image-type' => 'machine', :state => 'available'})
9
+ images[0]
10
+ end
11
+
12
+ instance = run "launching an instance of #{image.id}" do
13
+ instances = image.run :instance_type => 't1.micro', :tags => {'Name' => 'awsum.test'}
14
+ instances[0]
15
+ end
16
+
17
+ wait_for instance, 'running'
18
+
19
+ volume = run "attaching a volume to instance #{instance.id}" do
20
+ instance.create_volume(5, :device => '/dev/sdh', :tags => {'Name' => 'awsum.test'})
21
+ end
22
+ wait_for volume, 'in-use'
23
+
24
+ snapshot = run "taking a snapshot of volume #{volume.id}" do
25
+ volume.create_snapshot :tags => {'Name' => 'awsum.test'}
26
+ end
27
+ wait_for snapshot, 'completed'
28
+
29
+ run "deleting snapshot #{snapshot.id}" do
30
+ snapshot.delete
31
+ end
32
+
33
+ run "detaching volue #{volume.id}" do
34
+ volume.detach
35
+ end
36
+ wait_for volume, 'available'
37
+
38
+ run "deleting volue #{volume.id}" do
39
+ volume.delete
40
+ end
41
+
42
+ run "terminating instance #{instance.id}" do
43
+ instance.terminate
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,71 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'awsum/ec2'
3
+ require 'awsum/s3'
4
+
5
+ def functional(description, &block)
6
+ describe "Functional: #{description}" do
7
+ let(:keys) do
8
+ rc_file = File.join(File.expand_path('~'), '.awsumrc')
9
+ unless File.exists?(rc_file)
10
+ raise 'Unable to run functional specs with out access and secret keys. Place them in ~/.awsumrc'
11
+ end
12
+ YAML.load(File.read(rc_file))
13
+ end
14
+
15
+ let(:access_key) { keys['access_key'] }
16
+ let(:secret_key) { keys['secret_key'] }
17
+
18
+ instance_eval(&block)
19
+
20
+ after(:all) do
21
+ ec2.instances(:filter => {'instance-state-name' => ['running', 'stopped']},
22
+ :tags => {'Name' => 'awsum.test'}).each do |instance|
23
+ begin
24
+ instance.terminate
25
+ wait_for instance, 'terminated'
26
+ rescue
27
+ puts "Could not terminate instance #{instance.id}"
28
+ puts $!.inspect
29
+ puts $!.backtrace
30
+ end
31
+ end
32
+
33
+ ec2.volumes(:filter => {'status' => ['available', 'in-use']},
34
+ :tags => {'Name' => 'awsum.test'}).each do |volume|
35
+ begin
36
+ volume.delete!
37
+ rescue
38
+ puts "Could not delete volume #{volume.id}"
39
+ puts $!.inspect
40
+ puts $!.backtrace
41
+ end
42
+ end
43
+
44
+ ec2.snapshots(:filter => {'status' => ['pending', 'completed']},
45
+ :tags => {'Name' => 'awsum.test'}).each do |snapshot|
46
+ begin
47
+ snapshot.delete
48
+ rescue
49
+ puts "Could not delete snapshot #{snapshot.id}"
50
+ puts $!.inspect
51
+ puts $!.backtrace
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def run(description, &block)
59
+ puts description
60
+ yield
61
+ end
62
+
63
+ def wait_for(object, state = 'available')
64
+ print "waiting for #{state} on #{object.class}(#{object.id}) "
65
+ while (object.respond_to?(:state) ? object.state : object.status) != state
66
+ sleep 1
67
+ object.reload
68
+ print '.'
69
+ end
70
+ puts
71
+ end
@@ -11,9 +11,9 @@ require 'awsum/requestable'
11
11
  require 'awsum/support'
12
12
 
13
13
  module Awsum
14
- VERSION = "0.5"
14
+ VERSION = "0.5.1"
15
15
 
16
- API_VERSION = '2010-06-15'
16
+ API_VERSION = '2010-08-31'
17
17
  SIGNATURE_VERSION = 2
18
18
  end
19
19
 
@@ -9,6 +9,8 @@ require 'awsum/ec2/reserved_instance'
9
9
  require 'awsum/ec2/reserved_instances_offering'
10
10
  require 'awsum/ec2/security_group'
11
11
  require 'awsum/ec2/snapshot'
12
+ require 'awsum/ec2/state'
13
+ require 'awsum/ec2/tag'
12
14
  require 'awsum/ec2/volume'
13
15
 
14
16
  module Awsum
@@ -68,6 +70,8 @@ module Awsum
68
70
  # * <tt>:image_ids</tt> - array of Image id's, default: []
69
71
  # * <tt>:owners</tt> - array of owner id's, default: []
70
72
  # * <tt>:executable_by</tt> - array of user id's who have executable permission, default: []
73
+ # * <tt>:filter</tt> - hash of filters (e.g. :filter => {:architecture => 'i386'})
74
+ # * <tt>:tags</tt> - hash of tags (e.g. :tags => {:name => 'Test'})
71
75
  def images(options = {})
72
76
  options = {:image_ids => [], :owners => [], :executable_by => []}.merge(options)
73
77
  action = 'DescribeImages'
@@ -78,6 +82,7 @@ module Awsum
78
82
  params.merge!(array_to_params(options[:image_ids], "ImageId"))
79
83
  params.merge!(array_to_params(options[:owners], "Owner"))
80
84
  params.merge!(array_to_params(options[:executable_by], "ExecutableBy"))
85
+ params.merge!(parse_filters(options[:filter], options[:tags]))
81
86
 
82
87
  response = send_query_request(params)
83
88
  parser = Awsum::Ec2::ImageParser.new(self)
@@ -85,8 +90,8 @@ module Awsum
85
90
  end
86
91
 
87
92
  # Retrieve all Image(s) owned by you
88
- def my_images
89
- images :owners => 'self'
93
+ def my_images(options = {})
94
+ images options.merge(:owners => 'self')
90
95
  end
91
96
 
92
97
  # Retrieve a single Image
@@ -160,17 +165,27 @@ module Awsum
160
165
 
161
166
  response = send_query_request(params)
162
167
  parser = Awsum::Ec2::InstanceParser.new(self)
163
- parser.parse(response.body)
168
+ instances = parser.parse(response.body)
169
+ if options[:tags] && options[:tags].size > 0
170
+ create_tags instances.map{|i| i.id}, options[:tags]
171
+ end
172
+ instances
164
173
  end
165
174
  alias_method :launch_instances, :run_instances
166
175
 
167
176
  #Retrieve the information on a number of Instance(s)
177
+ #
178
+ # ===Options:
179
+ # * <tt>:filter</tt> - hash of filters (e.g. :filter => {:architecture => 'i386'})
180
+ # * <tt>:tags</tt> - hash of tags (e.g. :tags => {:name => 'Test'})
168
181
  def instances(*instance_ids)
182
+ options = instance_ids[-1].respond_to?(:keys) ? instance_ids.pop : {}
169
183
  action = 'DescribeInstances'
170
184
  params = {
171
185
  'Action' => action
172
186
  }
173
187
  params.merge!(array_to_params(instance_ids, 'InstanceId'))
188
+ params.merge!(parse_filters(options[:filter], options[:tags]))
174
189
 
175
190
  response = send_query_request(params)
176
191
  parser = Awsum::Ec2::InstanceParser.new(self)
@@ -219,13 +234,43 @@ module Awsum
219
234
  response.is_a?(Net::HTTPSuccess)
220
235
  end
221
236
 
237
+ def tags(filter = {})
238
+ action = 'DescribeTags'
239
+ params = {
240
+ 'Action' => action
241
+ }
242
+ params.merge!(parse_filters(filter))
243
+
244
+ response = send_query_request(params)
245
+ parser = Awsum::Ec2::TagParser.new(self)
246
+ parser.parse(response.body)
247
+ end
248
+
249
+ def create_tags(resource_ids, tags)
250
+ action = 'CreateTags'
251
+ params = {
252
+ 'Action' => action
253
+ }
254
+ params.merge!(array_to_params(resource_ids, 'ResourceId'))
255
+ params.merge!(parse_tag_keys(tags))
256
+
257
+ response = send_query_request(params)
258
+ response.is_a?(Net::HTTPSuccess)
259
+ end
260
+
222
261
  #Retrieve the information on a number of Volume(s)
262
+ #
263
+ # ===Options:
264
+ # * <tt>:filter</tt> - hash of filters (e.g. :filter => {'attachment.status' => 'attached'})
265
+ # * <tt>:tags</tt> - hash of tags (e.g. :tags => {:name => 'Test'})
223
266
  def volumes(*volume_ids)
267
+ options = volume_ids[-1].respond_to?(:keys) ? volume_ids.pop : {}
224
268
  action = 'DescribeVolumes'
225
269
  params = {
226
270
  'Action' => action
227
271
  }
228
272
  params.merge!(array_to_params(volume_ids, 'VolumeId'))
273
+ params.merge!(parse_filters(options[:filter], options[:tags]))
229
274
 
230
275
  response = send_query_request(params)
231
276
  parser = Awsum::Ec2::VolumeParser.new(self)
@@ -256,7 +301,11 @@ module Awsum
256
301
 
257
302
  response = send_query_request(params)
258
303
  parser = Awsum::Ec2::VolumeParser.new(self)
259
- parser.parse(response.body)[0]
304
+ volume = parser.parse(response.body)[0]
305
+ if options[:tags] && options[:tags].size > 0
306
+ create_tags volume.id, options[:tags]
307
+ end
308
+ volume
260
309
  end
261
310
 
262
311
  # Attach a volume to an instance
@@ -306,25 +355,36 @@ module Awsum
306
355
  end
307
356
 
308
357
  # Create a Snapshot of a Volume
309
- def create_snapshot(volume_id)
358
+ #
359
+ # ===Options:
360
+ # :description => A description for the stapshot
361
+ # :tags => A hash of tags to be associated with the snapshot
362
+ def create_snapshot(volume_id, options = {})
310
363
  action = 'CreateSnapshot'
311
364
  params = {
312
365
  'Action' => action,
313
366
  'VolumeId' => volume_id
314
367
  }
368
+ params['Description'] = options[:description] unless options[:description].blank?
315
369
 
316
370
  response = send_query_request(params)
317
371
  parser = Awsum::Ec2::SnapshotParser.new(self)
318
- parser.parse(response.body)[0]
372
+ snapshot = parser.parse(response.body)[0]
373
+ if options[:tags] && options[:tags].size > 0
374
+ create_tags snapshot.id, options[:tags]
375
+ end
376
+ snapshot
319
377
  end
320
378
 
321
379
  # List Snapshot(s)
322
380
  def snapshots(*snapshot_ids)
381
+ options = snapshot_ids[-1].respond_to?(:keys) ? snapshot_ids.pop : {}
323
382
  action = 'DescribeSnapshots'
324
383
  params = {
325
384
  'Action' => action
326
385
  }
327
386
  params.merge!(array_to_params(snapshot_ids, 'SnapshotId'))
387
+ params.merge!(parse_filters(options[:filter], options[:tags]))
328
388
 
329
389
  response = send_query_request(params)
330
390
  parser = Awsum::Ec2::SnapshotParser.new(self)
@@ -574,39 +634,21 @@ module Awsum
574
634
 
575
635
  # Authorize access on a specific security group
576
636
  #
577
- # ===Options:
637
+ # ===Usage
578
638
  # ====User/Group access
579
- # * <tt>:source_security_group_name</tt> - Name of the security group to authorize access to when operating on a user/group pair
580
- # * <tt>:source_security_group_owner_id</tt> - Owner of the security group to authorize access to when operating on a user/group pair
639
+ # ec2.authorize_security_group_ingress('security_group', {:ip_protocol => :tcp, :from_port => 80, :to_port => 80, :groups => [{:group_name => :authorized_group}]})
581
640
  # ====CIDR IP access
582
- # * <tt>:ip_protocol</tt> - IP protocol to authorize access to when operating on a CIDR IP (tcp, udp or icmp) (default: tcp)
583
- # * <tt>:from_port</tt> - Bottom of port range to authorize access to when operating on a CIDR IP. This contains the ICMP type if ICMP is being authorized.
584
- # * <tt>:to_port</tt> - Top of port range to authorize access to when operating on a CIDR IP. This contains the ICMP type if ICMP is being authorized.
585
- # * <tt>:cidr_ip</tt> - CIDR IP range to authorize access to when operating on a CIDR IP. (default: 0.0.0.0/0)
586
- def authorize_security_group_ingress(group_name, options = {})
587
- got_at_least_one_user_group_option = !options[:source_security_group_name].nil? || !options[:source_security_group_owner_id].nil?
588
- got_user_group_options = !options[:source_security_group_name].nil? && !options[:source_security_group_owner_id].nil?
589
- got_at_least_one_cidr_option = !options[:ip_protocol].nil? || !options[:from_port].nil? || !options[:to_port].nil? || !options[:cidr_ip].nil?
590
- #Add in defaults
591
- options = {:cidr_ip => '0.0.0.0/0'}.merge(options) if got_at_least_one_cidr_option
592
- options = {:ip_protocol => 'tcp'}.merge(options) if got_at_least_one_cidr_option
593
- got_cidr_options = !options[:ip_protocol].nil? && !options[:from_port].nil? && !options[:to_port].nil? && !options[:cidr_ip].nil?
594
- raise ArgumentError.new('Can only authorize user/group or CIDR IP, not both') if got_at_least_one_user_group_option && got_at_least_one_cidr_option
595
- raise ArgumentError.new('Need all user/group options when authorizing user/group access') if got_at_least_one_user_group_option && !got_user_group_options
596
- raise ArgumentError.new('Need all CIDR IP options when authorizing CIDR IP access') if got_at_least_one_cidr_option && !got_cidr_options
597
- raise ArgumentError.new('ip_protocol can only be one of tcp, udp or icmp') if got_at_least_one_cidr_option && !%w(tcp udp icmp).detect{|p| p == options[:ip_protocol] }
641
+ # ec2.authorize_security_group_ingress('security_group', {:ip_protocol => :tcp, :from_port => 80, :to_port => 80, :ip_ranges => [{:cidr_ip => '0.0.0.0/0'}]})
642
+ def authorize_security_group_ingress(group_name, arguments)
643
+ raise ArgumentError.new('Can only authorize user/group or CIDR IP, not both') if [arguments].flatten.detect{|a| a.has_key?(:ip_ranges) && a.has_key?(:groups)}
644
+ raise ArgumentError.new('ip_protocol can only be one of tcp, udp or icmp') if [arguments].flatten.detect{|a| !%w(tcp udp icmp).detect{|p| p == a[:ip_protocol].to_s } }
598
645
 
599
646
  action = 'AuthorizeSecurityGroupIngress'
600
647
  params = {
601
648
  'Action' => action,
602
649
  'GroupName' => group_name
603
650
  }
604
- params['SourceSecurityGroupName'] = options[:source_security_group_name] unless options[:source_security_group_name].nil?
605
- params['SourceSecurityGroupOwnerId'] = options[:source_security_group_owner_id] unless options[:source_security_group_owner_id].nil?
606
- params['IpProtocol'] = options[:ip_protocol] unless options[:ip_protocol].nil?
607
- params['FromPort'] = options[:from_port] unless options[:from_port].nil?
608
- params['ToPort'] = options[:to_port] unless options[:to_port].nil?
609
- params['CidrIp'] = options[:cidr_ip] unless options[:cidr_ip].nil?
651
+ params.merge!(array_to_params(arguments, 'IpPermissions'))
610
652
 
611
653
  response = send_query_request(params)
612
654
  response.is_a?(Net::HTTPSuccess)
@@ -614,39 +656,21 @@ module Awsum
614
656
 
615
657
  # Revoke access on a specific SecurityGroup
616
658
  #
617
- # ===Options:
659
+ # ===Usage
618
660
  # ====User/Group access
619
- # * <tt>:source_security_group_name</tt> - Name of the security group to authorize access to when operating on a user/group pair
620
- # * <tt>:source_security_group_owner_id</tt> - Owner of the security group to authorize access to when operating on a user/group pair
661
+ # ec2.revoke_security_group_ingress('security_group', {:ip_protocol => :tcp, :from_port => 80, :to_port => 80, :groups => [{:group_name => :revoked_group}]})
621
662
  # ====CIDR IP access
622
- # * <tt>:ip_protocol</tt> - IP protocol to authorize access to when operating on a CIDR IP (tcp, udp or icmp) (default: tcp)
623
- # * <tt>:from_port</tt> - Bottom of port range to authorize access to when operating on a CIDR IP. This contains the ICMP type if ICMP is being authorized.
624
- # * <tt>:to_port</tt> - Top of port range to authorize access to when operating on a CIDR IP. This contains the ICMP type if ICMP is being authorized.
625
- # * <tt>:cidr_ip</tt> - CIDR IP range to authorize access to when operating on a CIDR IP. (default: 0.0.0.0/0)
626
- def revoke_security_group_ingress(group_name, options = {})
627
- got_at_least_one_user_group_option = !options[:source_security_group_name].nil? || !options[:source_security_group_owner_id].nil?
628
- got_user_group_options = !options[:source_security_group_name].nil? && !options[:source_security_group_owner_id].nil?
629
- got_at_least_one_cidr_option = !options[:ip_protocol].nil? || !options[:from_port].nil? || !options[:to_port].nil? || !options[:cidr_ip].nil?
630
- #Add in defaults
631
- options = {:cidr_ip => '0.0.0.0/0'}.merge(options) if got_at_least_one_cidr_option
632
- options = {:ip_protocol => 'tcp'}.merge(options) if got_at_least_one_cidr_option
633
- got_cidr_options = !options[:ip_protocol].nil? && !options[:from_port].nil? && !options[:to_port].nil? && !options[:cidr_ip].nil?
634
- raise ArgumentError.new('Can only authorize user/group or CIDR IP, not both') if got_at_least_one_user_group_option && got_at_least_one_cidr_option
635
- raise ArgumentError.new('Need all user/group options when revoking user/group access') if got_at_least_one_user_group_option && !got_user_group_options
636
- raise ArgumentError.new('Need all CIDR IP options when revoking CIDR IP access') if got_at_least_one_cidr_option && !got_cidr_options
637
- raise ArgumentError.new('ip_protocol can only be one of tcp, udp or icmp') if got_at_least_one_cidr_option && !%w(tcp udp icmp).detect{|p| p == options[:ip_protocol] }
663
+ # ec2.revoke_security_group_ingress('security_group', {:ip_protocol => :tcp, :from_port => 80, :to_port => 80, :ip_ranges => [{:cidr_ip => '0.0.0.0/0'}]})
664
+ def revoke_security_group_ingress(group_name, arguments)
665
+ raise ArgumentError.new('Can only authorize user/group or CIDR IP, not both') if [arguments].flatten.detect{|a| a.has_key?(:ip_ranges) && a.has_key?(:groups)}
666
+ raise ArgumentError.new('ip_protocol can only be one of tcp, udp or icmp') if [arguments].flatten.detect{|a| !%w(tcp udp icmp).detect{|p| p == a[:ip_protocol].to_s } }
638
667
 
639
668
  action = 'RevokeSecurityGroupIngress'
640
669
  params = {
641
670
  'Action' => action,
642
671
  'GroupName' => group_name
643
672
  }
644
- params['SourceSecurityGroupName'] = options[:source_security_group_name] unless options[:source_security_group_name].nil?
645
- params['SourceSecurityGroupOwnerId'] = options[:source_security_group_owner_id] unless options[:source_security_group_owner_id].nil?
646
- params['IpProtocol'] = options[:ip_protocol] unless options[:ip_protocol].nil?
647
- params['FromPort'] = options[:from_port] unless options[:from_port].nil?
648
- params['ToPort'] = options[:to_port] unless options[:to_port].nil?
649
- params['CidrIp'] = options[:cidr_ip] unless options[:cidr_ip].nil?
673
+ params.merge!(array_to_params(arguments, 'IpPermissions'))
650
674
 
651
675
  response = send_query_request(params)
652
676
  response.is_a?(Net::HTTPSuccess)
@@ -686,29 +710,22 @@ module Awsum
686
710
  # Purchase reserved instances
687
711
  #
688
712
  # ===Options:
689
- # * <tt>:reserved_instances_offering_ids</tt> - A single reserved instance offering id (or an array of instance ids)
690
- # * <tt>:instance_counts</tt> - A number of reserved instances to purchase (or an array of counts per instance in the reserved_instances_offering_ids array)
713
+ # * <tt>:reserved_instances_offering_id</tt> - A single reserved instance offering id
714
+ # * <tt>:instance_count</tt> - A number of reserved instances to purchase
691
715
  #
692
716
  # ===Example
693
717
  # ec2.purchase_reserved_instances_offering('reservation-123456', 1)
694
- # or
695
- # ec2.purchase_reserved_instances_offering(['reservation-123456', 'reservation-654321'], [1, 2])
696
- def purchase_reserved_instances_offering(reserved_instances_offering_ids, instance_counts = 1)
718
+ def purchase_reserved_instances_offering(reserved_instances_offering_id, instance_count = 1)
697
719
  action = 'PurchaseReservedInstancesOffering'
698
720
  params = {
699
721
  'Action' => action,
700
722
  }
701
- params.merge!(array_to_params([instance_counts].flatten, 'InstanceCount'))
702
- params.merge!(array_to_params([reserved_instances_offering_ids].flatten, 'ReservedInstancesOfferingId'))
723
+ params['ReservedInstancesOfferingId'] = reserved_instances_offering_id
724
+ params['InstanceCount'] = instance_count
703
725
 
704
726
  response = send_query_request(params)
705
727
  parser = Awsum::Ec2::PurchaseReservedInstancesOfferingParser.new(self)
706
728
  result = parser.parse(response.body)
707
- if reserved_instances_offering_ids.is_a?(Array)
708
- reserved_instances(*result)
709
- else
710
- reserved_instance(result)
711
- end
712
729
  end
713
730
 
714
731
  def reserved_instances(*reserved_instances_ids)