awsum 0.5 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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)