awsum 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. data/.autotest +1 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +15 -0
  5. data/Gemfile.lock +44 -0
  6. data/LICENSE +19 -0
  7. data/README.rdoc +42 -0
  8. data/Rakefile +75 -0
  9. data/awsum.gemspec +199 -0
  10. data/lib/awsum.rb +20 -0
  11. data/lib/awsum/ec2.rb +741 -0
  12. data/lib/awsum/ec2/address.rb +67 -0
  13. data/lib/awsum/ec2/availability_zone.rb +16 -0
  14. data/lib/awsum/ec2/image.rb +62 -0
  15. data/lib/awsum/ec2/instance.rb +57 -0
  16. data/lib/awsum/ec2/keypair.rb +16 -0
  17. data/lib/awsum/ec2/parsers/address_parser.rb +61 -0
  18. data/lib/awsum/ec2/parsers/availability_zone_parser.rb +57 -0
  19. data/lib/awsum/ec2/parsers/image_parser.rb +74 -0
  20. data/lib/awsum/ec2/parsers/instance_parser.rb +90 -0
  21. data/lib/awsum/ec2/parsers/keypair_parser.rb +64 -0
  22. data/lib/awsum/ec2/parsers/purchase_reserved_instances_offering_parser.rb +34 -0
  23. data/lib/awsum/ec2/parsers/region_parser.rb +56 -0
  24. data/lib/awsum/ec2/parsers/register_image_parser.rb +34 -0
  25. data/lib/awsum/ec2/parsers/reserved_instance_parser.rb +64 -0
  26. data/lib/awsum/ec2/parsers/reserved_instances_offering_parser.rb +63 -0
  27. data/lib/awsum/ec2/parsers/security_group_parser.rb +106 -0
  28. data/lib/awsum/ec2/parsers/snapshot_parser.rb +64 -0
  29. data/lib/awsum/ec2/parsers/volume_parser.rb +77 -0
  30. data/lib/awsum/ec2/region.rb +54 -0
  31. data/lib/awsum/ec2/reserved_instance.rb +24 -0
  32. data/lib/awsum/ec2/reserved_instances_offering.rb +20 -0
  33. data/lib/awsum/ec2/security_group.rb +74 -0
  34. data/lib/awsum/ec2/snapshot.rb +23 -0
  35. data/lib/awsum/ec2/volume.rb +65 -0
  36. data/lib/awsum/error.rb +53 -0
  37. data/lib/awsum/net_fix.rb +100 -0
  38. data/lib/awsum/parser.rb +18 -0
  39. data/lib/awsum/requestable.rb +216 -0
  40. data/lib/awsum/s3.rb +220 -0
  41. data/lib/awsum/s3/bucket.rb +28 -0
  42. data/lib/awsum/s3/headers.rb +24 -0
  43. data/lib/awsum/s3/object.rb +138 -0
  44. data/lib/awsum/s3/parsers/bucket_parser.rb +43 -0
  45. data/lib/awsum/support.rb +94 -0
  46. data/spec/fixtures/ec2/addresses.xml +10 -0
  47. data/spec/fixtures/ec2/allocate_address.xml +5 -0
  48. data/spec/fixtures/ec2/associate_address.xml +5 -0
  49. data/spec/fixtures/ec2/attach_volume.xml +9 -0
  50. data/spec/fixtures/ec2/authorize_ip_access.xml +5 -0
  51. data/spec/fixtures/ec2/authorize_owner_group_access.xml +5 -0
  52. data/spec/fixtures/ec2/authorize_owner_group_access_error.xml +2 -0
  53. data/spec/fixtures/ec2/availability_zones.xml +16 -0
  54. data/spec/fixtures/ec2/available_volume.xml +14 -0
  55. data/spec/fixtures/ec2/create_key_pair.xml +29 -0
  56. data/spec/fixtures/ec2/create_security_group.xml +5 -0
  57. data/spec/fixtures/ec2/create_snapshot.xml +9 -0
  58. data/spec/fixtures/ec2/create_volume.xml +10 -0
  59. data/spec/fixtures/ec2/delete_key_pair.xml +5 -0
  60. data/spec/fixtures/ec2/delete_security_group.xml +5 -0
  61. data/spec/fixtures/ec2/delete_snapshot.xml +5 -0
  62. data/spec/fixtures/ec2/delete_volume.xml +5 -0
  63. data/spec/fixtures/ec2/deregister_image.xml +5 -0
  64. data/spec/fixtures/ec2/detach_volume.xml +9 -0
  65. data/spec/fixtures/ec2/disassociate_address.xml +5 -0
  66. data/spec/fixtures/ec2/image.xml +15 -0
  67. data/spec/fixtures/ec2/images.xml +77 -0
  68. data/spec/fixtures/ec2/instance.xml +36 -0
  69. data/spec/fixtures/ec2/instances.xml +88 -0
  70. data/spec/fixtures/ec2/internal_error.xml +2 -0
  71. data/spec/fixtures/ec2/invalid_amiid_error.xml +2 -0
  72. data/spec/fixtures/ec2/invalid_request_error.xml +2 -0
  73. data/spec/fixtures/ec2/key_pairs.xml +10 -0
  74. data/spec/fixtures/ec2/purchase_reserved_instances_offering.xml +5 -0
  75. data/spec/fixtures/ec2/purchase_reserved_instances_offerings.xml +6 -0
  76. data/spec/fixtures/ec2/regions.xml +14 -0
  77. data/spec/fixtures/ec2/register_image.xml +5 -0
  78. data/spec/fixtures/ec2/release_address.xml +5 -0
  79. data/spec/fixtures/ec2/reserved_instances.xml +18 -0
  80. data/spec/fixtures/ec2/reserved_instances_offering.xml +15 -0
  81. data/spec/fixtures/ec2/reserved_instances_offerings.xml +276 -0
  82. data/spec/fixtures/ec2/revoke_ip_access.xml +5 -0
  83. data/spec/fixtures/ec2/revoke_owner_group_access.xml +5 -0
  84. data/spec/fixtures/ec2/run_instances.xml +30 -0
  85. data/spec/fixtures/ec2/security_groups.xml +159 -0
  86. data/spec/fixtures/ec2/snapshots.xml +13 -0
  87. data/spec/fixtures/ec2/terminate_instances.xml +17 -0
  88. data/spec/fixtures/ec2/unassociated_address.xml +10 -0
  89. data/spec/fixtures/ec2/volumes.xml +23 -0
  90. data/spec/fixtures/errors/invalid_parameter_value.xml +2 -0
  91. data/spec/fixtures/s3/buckets.xml +2 -0
  92. data/spec/fixtures/s3/copy_failure.xml +23 -0
  93. data/spec/fixtures/s3/invalid_request_signature.xml +5 -0
  94. data/spec/fixtures/s3/keys.xml +2 -0
  95. data/spec/lib/awsum/ec2/address_spec.rb +149 -0
  96. data/spec/lib/awsum/ec2/availability_zones_spec.rb +21 -0
  97. data/spec/lib/awsum/ec2/image_spec.rb +92 -0
  98. data/spec/lib/awsum/ec2/instance_spec.rb +125 -0
  99. data/spec/lib/awsum/ec2/keypair_spec.rb +55 -0
  100. data/spec/lib/awsum/ec2/parsers/address_parser_spec.rb +51 -0
  101. data/spec/lib/awsum/ec2/parsers/availability_zone_parser_spec.rb +28 -0
  102. data/spec/lib/awsum/ec2/parsers/image_parser_spec.rb +66 -0
  103. data/spec/lib/awsum/ec2/parsers/instance_parser_spec.rb +75 -0
  104. data/spec/lib/awsum/ec2/parsers/keypair_parser_spec.rb +74 -0
  105. data/spec/lib/awsum/ec2/parsers/purchase_reserved_instances_offering_parser_spec.rb +14 -0
  106. data/spec/lib/awsum/ec2/parsers/region_parser_spec.rb +27 -0
  107. data/spec/lib/awsum/ec2/parsers/register_image_parser_spec.rb +15 -0
  108. data/spec/lib/awsum/ec2/parsers/reserved_instance_parser_spec.rb +35 -0
  109. data/spec/lib/awsum/ec2/parsers/reserved_instances_offering_parser_spec.rb +32 -0
  110. data/spec/lib/awsum/ec2/parsers/security_group_parser_spec.rb +78 -0
  111. data/spec/lib/awsum/ec2/parsers/snapshot_parser_spec.rb +30 -0
  112. data/spec/lib/awsum/ec2/parsers/volume_parser_spec.rb +35 -0
  113. data/spec/lib/awsum/ec2/region_spec.rb +73 -0
  114. data/spec/lib/awsum/ec2/reserved_instance_spec.rb +61 -0
  115. data/spec/lib/awsum/ec2/reserved_instances_offering_spec.rb +33 -0
  116. data/spec/lib/awsum/ec2/security_group_spec.rb +179 -0
  117. data/spec/lib/awsum/ec2/snapshots_spec.rb +69 -0
  118. data/spec/lib/awsum/ec2/volume_spec.rb +107 -0
  119. data/spec/lib/awsum/ec2_spec.rb +6 -0
  120. data/spec/lib/awsum/error_spec.rb +31 -0
  121. data/spec/lib/awsum/requestable_spec.rb +126 -0
  122. data/spec/lib/awsum/s3/bucket_spec.rb +95 -0
  123. data/spec/lib/awsum/s3/object_spec.rb +128 -0
  124. data/spec/lib/awsum/s3/parsers/bucket_parser_spec.rb +41 -0
  125. data/spec/lib/awsum/s3/parsers/object_parser_spec.rb +41 -0
  126. data/spec/spec_helper.rb +16 -0
  127. metadata +240 -0
@@ -0,0 +1 @@
1
+ require 'autotest/redgreen'
@@ -0,0 +1,5 @@
1
+ doc
2
+ *.swp
3
+ coverage
4
+ .bundle/
5
+ pkg/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rake"
4
+ gem "bundler", "~> 1.0.0"
5
+
6
+ group :development do
7
+ gem "jeweler"
8
+ gem "rspec", ">= 2.0.0.beta.22"
9
+ gem "fakeweb"
10
+
11
+ gem "shoulda"
12
+ gem "mocha"
13
+ gem "rcov"
14
+ gem "timecop"
15
+ end
@@ -0,0 +1,44 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.2)
5
+ fakeweb (1.2.8)
6
+ gemcutter (0.6.1)
7
+ git (1.2.5)
8
+ jeweler (1.4.0)
9
+ gemcutter (>= 0.1.0)
10
+ git (>= 1.2.5)
11
+ rubyforge (>= 2.0.0)
12
+ json_pure (1.4.6)
13
+ mocha (0.9.8)
14
+ rake
15
+ rake (0.8.7)
16
+ 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)
29
+ shoulda (2.11.3)
30
+ timecop (0.3.5)
31
+
32
+ PLATFORMS
33
+ ruby
34
+
35
+ DEPENDENCIES
36
+ bundler (~> 1.0.0)
37
+ fakeweb
38
+ jeweler
39
+ mocha
40
+ rake
41
+ rcov
42
+ rspec (>= 2.0.0.beta.22)
43
+ shoulda
44
+ timecop
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Internuity Ltd, Andrew Timberlake <andrew@andrewtimberlake.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,42 @@
1
+ =Awsum
2
+
3
+ Awsum (Pronounced Awesome) is a library for working with Amazon web services.
4
+ The concept of Awsum is to expose the AWS library is the most ruby way possible
5
+ allowing you to work with objects in a very natural way.
6
+
7
+ ==Quick Start
8
+
9
+ #Create a snapshot for every volume of every instance you own
10
+ ec2 = Awsum::Ec2.new(<access key>, <secret key>)
11
+ ec2.instances.each do |instance|
12
+ instance.volumes.each do |volume|
13
+ volume.create_snapshot
14
+ end
15
+ end
16
+
17
+ ==Working with different Regions
18
+ You can use blocks to wrap your calls for a specific Region
19
+ ec2.region('eu-west-1').use do
20
+ #Run an instance in the eu-west-1 region
21
+ run_instance(...)
22
+ end
23
+
24
+ ==Using the library on an EC2 instance
25
+
26
+ There are two methods specifically for using a library on an EC2 instance
27
+ Awsum::Ec2#me
28
+ Awsum::Ec2#user_data
29
+
30
+ To extend the quick start example, you could do
31
+
32
+ #Create a snapshot of every volume of the currently running instance
33
+ ec2 = Awsum::Ec2.new(<access key>, <secret key>)
34
+ ec2.me.volumes.each do |volume|
35
+ volume.create_snapshot
36
+ end
37
+
38
+ ==Note:
39
+
40
+ Awsum is currently under active development and only supports EC2 at the moment.
41
+
42
+ Once EC2 is complete, I will focus on S3, SQS, CloudSpace and then others
@@ -0,0 +1,75 @@
1
+ require 'bundler'
2
+ begin
3
+ Bundler.setup(:default, :development)
4
+ rescue Bundler::BundlerError => e
5
+ $stderr.puts e.message
6
+ $stderr.puts "Run `bundle install` to install missing gems"
7
+ exit e.status_code
8
+ end
9
+
10
+ require 'rake/rdoctask'
11
+ require 'rake/testtask'
12
+
13
+ $LOAD_PATH.unshift('lib')
14
+ require 'awsum'
15
+
16
+ begin
17
+ require 'jeweler'
18
+
19
+ Jeweler::Tasks.new do |gem|
20
+ gem.name = 'awsum'
21
+ gem.summary = 'A library for working with Amazon Web Services in the most natural rubyish way'
22
+ gem.email = 'andrew@andrewtimberlake.com'
23
+ gem.homepage = 'http://andrewtimberlake.com/projects/awsum'
24
+ gem.authors = ['Andrew Timberlake']
25
+ gem.version = Awsum::VERSION
26
+
27
+ #gem.add_dependency
28
+ gem.add_development_dependency('rspec', '>= 2.0.0.beta.22')
29
+ end
30
+
31
+ Jeweler::GemcutterTasks.new
32
+
33
+ task :default => :spec
34
+ rescue LoadError
35
+ puts "Jeweler not available. Install it with: gem install jeweler"
36
+ end
37
+
38
+ desc 'Run code coverage'
39
+ task :coverage do |t|
40
+ puts `rcov -T #{Dir.glob('test/**/test_*.rb').join(' ')}`
41
+ end
42
+
43
+ desc 'Start an IRB session with all necessary files required.'
44
+ task :shell do |t|
45
+ chdir File.dirname(__FILE__)
46
+ exec 'irb -I lib/ -I lib/awsum -r rubygems -r awsum'
47
+ end
48
+
49
+ desc 'Generate documentation.'
50
+ Rake::RDocTask.new(:rdoc) do |rdoc|
51
+ rdoc.rdoc_dir = 'doc'
52
+ rdoc.title = 'AWSum'
53
+ rdoc.options << '--line-numbers' << '--inline-source'
54
+ rdoc.rdoc_files.include('README*')
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
57
+
58
+ desc 'Clean up files.'
59
+ task :clean do |t|
60
+ FileUtils.rm_rf "doc"
61
+ FileUtils.rm_rf "tmp"
62
+ FileUtils.rm_rf "pkg"
63
+ end
64
+
65
+ require 'rspec/core/rake_task'
66
+ RSpec::Core::RakeTask.new do |t|
67
+ t.pattern = "./spec/lib/**/*_spec.rb"
68
+ end
69
+
70
+ namespace :spec do
71
+ desc "Run RSpec integration code examples (LIVE runs against Amazon AWS)"
72
+ RSpec::Core::RakeTask.new(:integration) do |t|
73
+ t.pattern = "./spec/functional/**/*_spec.rb"
74
+ end
75
+ end
@@ -0,0 +1,199 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{awsum}
8
+ s.version = "0.5"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Andrew Timberlake"]
12
+ s.date = %q{2010-09-19}
13
+ s.email = %q{andrew@andrewtimberlake.com}
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".autotest",
20
+ ".gitignore",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "awsum.gemspec",
28
+ "lib/awsum.rb",
29
+ "lib/awsum/ec2.rb",
30
+ "lib/awsum/ec2/address.rb",
31
+ "lib/awsum/ec2/availability_zone.rb",
32
+ "lib/awsum/ec2/image.rb",
33
+ "lib/awsum/ec2/instance.rb",
34
+ "lib/awsum/ec2/keypair.rb",
35
+ "lib/awsum/ec2/parsers/address_parser.rb",
36
+ "lib/awsum/ec2/parsers/availability_zone_parser.rb",
37
+ "lib/awsum/ec2/parsers/image_parser.rb",
38
+ "lib/awsum/ec2/parsers/instance_parser.rb",
39
+ "lib/awsum/ec2/parsers/keypair_parser.rb",
40
+ "lib/awsum/ec2/parsers/purchase_reserved_instances_offering_parser.rb",
41
+ "lib/awsum/ec2/parsers/region_parser.rb",
42
+ "lib/awsum/ec2/parsers/register_image_parser.rb",
43
+ "lib/awsum/ec2/parsers/reserved_instance_parser.rb",
44
+ "lib/awsum/ec2/parsers/reserved_instances_offering_parser.rb",
45
+ "lib/awsum/ec2/parsers/security_group_parser.rb",
46
+ "lib/awsum/ec2/parsers/snapshot_parser.rb",
47
+ "lib/awsum/ec2/parsers/volume_parser.rb",
48
+ "lib/awsum/ec2/region.rb",
49
+ "lib/awsum/ec2/reserved_instance.rb",
50
+ "lib/awsum/ec2/reserved_instances_offering.rb",
51
+ "lib/awsum/ec2/security_group.rb",
52
+ "lib/awsum/ec2/snapshot.rb",
53
+ "lib/awsum/ec2/volume.rb",
54
+ "lib/awsum/error.rb",
55
+ "lib/awsum/net_fix.rb",
56
+ "lib/awsum/parser.rb",
57
+ "lib/awsum/requestable.rb",
58
+ "lib/awsum/s3.rb",
59
+ "lib/awsum/s3/bucket.rb",
60
+ "lib/awsum/s3/headers.rb",
61
+ "lib/awsum/s3/object.rb",
62
+ "lib/awsum/s3/parsers/bucket_parser.rb",
63
+ "lib/awsum/support.rb",
64
+ "spec/fixtures/ec2/addresses.xml",
65
+ "spec/fixtures/ec2/allocate_address.xml",
66
+ "spec/fixtures/ec2/associate_address.xml",
67
+ "spec/fixtures/ec2/attach_volume.xml",
68
+ "spec/fixtures/ec2/authorize_ip_access.xml",
69
+ "spec/fixtures/ec2/authorize_owner_group_access.xml",
70
+ "spec/fixtures/ec2/authorize_owner_group_access_error.xml",
71
+ "spec/fixtures/ec2/availability_zones.xml",
72
+ "spec/fixtures/ec2/available_volume.xml",
73
+ "spec/fixtures/ec2/create_key_pair.xml",
74
+ "spec/fixtures/ec2/create_security_group.xml",
75
+ "spec/fixtures/ec2/create_snapshot.xml",
76
+ "spec/fixtures/ec2/create_volume.xml",
77
+ "spec/fixtures/ec2/delete_key_pair.xml",
78
+ "spec/fixtures/ec2/delete_security_group.xml",
79
+ "spec/fixtures/ec2/delete_snapshot.xml",
80
+ "spec/fixtures/ec2/delete_volume.xml",
81
+ "spec/fixtures/ec2/deregister_image.xml",
82
+ "spec/fixtures/ec2/detach_volume.xml",
83
+ "spec/fixtures/ec2/disassociate_address.xml",
84
+ "spec/fixtures/ec2/image.xml",
85
+ "spec/fixtures/ec2/images.xml",
86
+ "spec/fixtures/ec2/instance.xml",
87
+ "spec/fixtures/ec2/instances.xml",
88
+ "spec/fixtures/ec2/internal_error.xml",
89
+ "spec/fixtures/ec2/invalid_amiid_error.xml",
90
+ "spec/fixtures/ec2/invalid_request_error.xml",
91
+ "spec/fixtures/ec2/key_pairs.xml",
92
+ "spec/fixtures/ec2/purchase_reserved_instances_offering.xml",
93
+ "spec/fixtures/ec2/purchase_reserved_instances_offerings.xml",
94
+ "spec/fixtures/ec2/regions.xml",
95
+ "spec/fixtures/ec2/register_image.xml",
96
+ "spec/fixtures/ec2/release_address.xml",
97
+ "spec/fixtures/ec2/reserved_instances.xml",
98
+ "spec/fixtures/ec2/reserved_instances_offering.xml",
99
+ "spec/fixtures/ec2/reserved_instances_offerings.xml",
100
+ "spec/fixtures/ec2/revoke_ip_access.xml",
101
+ "spec/fixtures/ec2/revoke_owner_group_access.xml",
102
+ "spec/fixtures/ec2/run_instances.xml",
103
+ "spec/fixtures/ec2/security_groups.xml",
104
+ "spec/fixtures/ec2/snapshots.xml",
105
+ "spec/fixtures/ec2/terminate_instances.xml",
106
+ "spec/fixtures/ec2/unassociated_address.xml",
107
+ "spec/fixtures/ec2/volumes.xml",
108
+ "spec/fixtures/errors/invalid_parameter_value.xml",
109
+ "spec/fixtures/s3/buckets.xml",
110
+ "spec/fixtures/s3/copy_failure.xml",
111
+ "spec/fixtures/s3/invalid_request_signature.xml",
112
+ "spec/fixtures/s3/keys.xml",
113
+ "spec/lib/awsum/ec2/address_spec.rb",
114
+ "spec/lib/awsum/ec2/availability_zones_spec.rb",
115
+ "spec/lib/awsum/ec2/image_spec.rb",
116
+ "spec/lib/awsum/ec2/instance_spec.rb",
117
+ "spec/lib/awsum/ec2/keypair_spec.rb",
118
+ "spec/lib/awsum/ec2/parsers/address_parser_spec.rb",
119
+ "spec/lib/awsum/ec2/parsers/availability_zone_parser_spec.rb",
120
+ "spec/lib/awsum/ec2/parsers/image_parser_spec.rb",
121
+ "spec/lib/awsum/ec2/parsers/instance_parser_spec.rb",
122
+ "spec/lib/awsum/ec2/parsers/keypair_parser_spec.rb",
123
+ "spec/lib/awsum/ec2/parsers/purchase_reserved_instances_offering_parser_spec.rb",
124
+ "spec/lib/awsum/ec2/parsers/region_parser_spec.rb",
125
+ "spec/lib/awsum/ec2/parsers/register_image_parser_spec.rb",
126
+ "spec/lib/awsum/ec2/parsers/reserved_instance_parser_spec.rb",
127
+ "spec/lib/awsum/ec2/parsers/reserved_instances_offering_parser_spec.rb",
128
+ "spec/lib/awsum/ec2/parsers/security_group_parser_spec.rb",
129
+ "spec/lib/awsum/ec2/parsers/snapshot_parser_spec.rb",
130
+ "spec/lib/awsum/ec2/parsers/volume_parser_spec.rb",
131
+ "spec/lib/awsum/ec2/region_spec.rb",
132
+ "spec/lib/awsum/ec2/reserved_instance_spec.rb",
133
+ "spec/lib/awsum/ec2/reserved_instances_offering_spec.rb",
134
+ "spec/lib/awsum/ec2/security_group_spec.rb",
135
+ "spec/lib/awsum/ec2/snapshots_spec.rb",
136
+ "spec/lib/awsum/ec2/volume_spec.rb",
137
+ "spec/lib/awsum/ec2_spec.rb",
138
+ "spec/lib/awsum/error_spec.rb",
139
+ "spec/lib/awsum/requestable_spec.rb",
140
+ "spec/lib/awsum/s3/bucket_spec.rb",
141
+ "spec/lib/awsum/s3/object_spec.rb",
142
+ "spec/lib/awsum/s3/parsers/bucket_parser_spec.rb",
143
+ "spec/lib/awsum/s3/parsers/object_parser_spec.rb",
144
+ "spec/spec_helper.rb"
145
+ ]
146
+ s.homepage = %q{http://andrewtimberlake.com/projects/awsum}
147
+ s.rdoc_options = ["--charset=UTF-8"]
148
+ s.require_paths = ["lib"]
149
+ s.rubygems_version = %q{1.3.7}
150
+ s.summary = %q{A library for working with Amazon Web Services in the most natural rubyish way}
151
+ s.test_files = [
152
+ "spec/lib/awsum/s3/parsers/object_parser_spec.rb",
153
+ "spec/lib/awsum/s3/parsers/bucket_parser_spec.rb",
154
+ "spec/lib/awsum/s3/object_spec.rb",
155
+ "spec/lib/awsum/s3/bucket_spec.rb",
156
+ "spec/lib/awsum/ec2/reserved_instance_spec.rb",
157
+ "spec/lib/awsum/ec2/region_spec.rb",
158
+ "spec/lib/awsum/ec2/availability_zones_spec.rb",
159
+ "spec/lib/awsum/ec2/parsers/register_image_parser_spec.rb",
160
+ "spec/lib/awsum/ec2/parsers/security_group_parser_spec.rb",
161
+ "spec/lib/awsum/ec2/parsers/image_parser_spec.rb",
162
+ "spec/lib/awsum/ec2/parsers/snapshot_parser_spec.rb",
163
+ "spec/lib/awsum/ec2/parsers/region_parser_spec.rb",
164
+ "spec/lib/awsum/ec2/parsers/availability_zone_parser_spec.rb",
165
+ "spec/lib/awsum/ec2/parsers/reserved_instance_parser_spec.rb",
166
+ "spec/lib/awsum/ec2/parsers/address_parser_spec.rb",
167
+ "spec/lib/awsum/ec2/parsers/purchase_reserved_instances_offering_parser_spec.rb",
168
+ "spec/lib/awsum/ec2/parsers/volume_parser_spec.rb",
169
+ "spec/lib/awsum/ec2/parsers/reserved_instances_offering_parser_spec.rb",
170
+ "spec/lib/awsum/ec2/parsers/keypair_parser_spec.rb",
171
+ "spec/lib/awsum/ec2/parsers/instance_parser_spec.rb",
172
+ "spec/lib/awsum/ec2/image_spec.rb",
173
+ "spec/lib/awsum/ec2/address_spec.rb",
174
+ "spec/lib/awsum/ec2/keypair_spec.rb",
175
+ "spec/lib/awsum/ec2/snapshots_spec.rb",
176
+ "spec/lib/awsum/ec2/volume_spec.rb",
177
+ "spec/lib/awsum/ec2/instance_spec.rb",
178
+ "spec/lib/awsum/ec2/security_group_spec.rb",
179
+ "spec/lib/awsum/ec2/reserved_instances_offering_spec.rb",
180
+ "spec/lib/awsum/ec2_spec.rb",
181
+ "spec/lib/awsum/error_spec.rb",
182
+ "spec/lib/awsum/requestable_spec.rb",
183
+ "spec/spec_helper.rb"
184
+ ]
185
+
186
+ if s.respond_to? :specification_version then
187
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
188
+ s.specification_version = 3
189
+
190
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
191
+ s.add_development_dependency(%q<rspec>, [">= 2.0.0.beta.22"])
192
+ else
193
+ s.add_dependency(%q<rspec>, [">= 2.0.0.beta.22"])
194
+ end
195
+ else
196
+ s.add_dependency(%q<rspec>, [">= 2.0.0.beta.22"])
197
+ end
198
+ end
199
+
@@ -0,0 +1,20 @@
1
+ # AWSum is a library facilitating access to Amazon's web services in (hopefully)
2
+ # a very object-oriented, ruby way.
3
+ #
4
+ # Author:: Andrew Timberlake
5
+ # Copyright:: Copyright (c) 2009 Internuity Ltd
6
+ # Licence:: MIT License (http://www.opensource.org/licenses/mit-license.php)
7
+ #
8
+
9
+ require 'awsum/parser'
10
+ require 'awsum/requestable'
11
+ require 'awsum/support'
12
+
13
+ module Awsum
14
+ VERSION = "0.5"
15
+
16
+ API_VERSION = '2010-06-15'
17
+ SIGNATURE_VERSION = 2
18
+ end
19
+
20
+
@@ -0,0 +1,741 @@
1
+ require 'awsum'
2
+ require 'awsum/ec2/address'
3
+ require 'awsum/ec2/availability_zone'
4
+ require 'awsum/ec2/image'
5
+ require 'awsum/ec2/instance'
6
+ require 'awsum/ec2/keypair'
7
+ require 'awsum/ec2/region'
8
+ require 'awsum/ec2/reserved_instance'
9
+ require 'awsum/ec2/reserved_instances_offering'
10
+ require 'awsum/ec2/security_group'
11
+ require 'awsum/ec2/snapshot'
12
+ require 'awsum/ec2/volume'
13
+
14
+ module Awsum
15
+ # Handles all interaction with Amazon EC2
16
+ #
17
+ # ==Getting Started
18
+ # Create an Awsum::Ec2 object and begin calling methods on it.
19
+ # require 'rubygems'
20
+ # require 'awsum'
21
+ # ec2 = Awsum::Ec2.new('your access id', 'your secret key')
22
+ # images = ec2.my_images
23
+ # ...
24
+ #
25
+ # All calls to EC2 can be done directly in this class, or through a more object oriented way through the various returned classes
26
+ #
27
+ # ==Examples
28
+ # ec2.image('ami-ABCDEF').run
29
+ #
30
+ # ec2.instance('i-123456789').volumes.each do |vol|
31
+ # vol.create_snapsot
32
+ # end
33
+ #
34
+ # ec2.regions.each do |region|
35
+ # region.use
36
+ # images.each do |img|
37
+ # puts "#{img.id} - #{region.name}"
38
+ # end
39
+ # end
40
+ # end
41
+ #
42
+ # ==Errors
43
+ # All methods will raise an Awsum::Error if an error is returned from Amazon
44
+ #
45
+ # ==Missing Methods
46
+ # * ConfirmProductInstance
47
+ # * ModifyImageAttribute
48
+ # * DescribeImageAttribute
49
+ # * ResetImageAttribute
50
+ # If you need any of this functionality, please consider getting involved and help complete this library.
51
+ class Ec2
52
+ include Awsum::Requestable
53
+
54
+ # Create an new ec2 instance
55
+ #
56
+ # The access_key and secret_key are both required to do any meaningful work.
57
+ #
58
+ # If you want to get these keys from environment variables, you can do that in your code as follows:
59
+ # ec2 = Awsum::Ec2.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'])
60
+ def initialize(access_key = nil, secret_key = nil)
61
+ @access_key = access_key
62
+ @secret_key = secret_key
63
+ end
64
+
65
+ # Retrieve a list of available Images
66
+ #
67
+ # ===Options:
68
+ # * <tt>:image_ids</tt> - array of Image id's, default: []
69
+ # * <tt>:owners</tt> - array of owner id's, default: []
70
+ # * <tt>:executable_by</tt> - array of user id's who have executable permission, default: []
71
+ def images(options = {})
72
+ options = {:image_ids => [], :owners => [], :executable_by => []}.merge(options)
73
+ action = 'DescribeImages'
74
+ params = {
75
+ 'Action' => action
76
+ }
77
+ #Add options
78
+ params.merge!(array_to_params(options[:image_ids], "ImageId"))
79
+ params.merge!(array_to_params(options[:owners], "Owner"))
80
+ params.merge!(array_to_params(options[:executable_by], "ExecutableBy"))
81
+
82
+ response = send_query_request(params)
83
+ parser = Awsum::Ec2::ImageParser.new(self)
84
+ parser.parse(response.body)
85
+ end
86
+
87
+ # Retrieve all Image(s) owned by you
88
+ def my_images
89
+ images :owners => 'self'
90
+ end
91
+
92
+ # Retrieve a single Image
93
+ def image(image_id)
94
+ images(:image_ids => [image_id])[0]
95
+ end
96
+
97
+ # Register an Image
98
+ def register_image(image_location)
99
+ action = 'RegisterImage'
100
+ params = {
101
+ 'Action' => action,
102
+ 'ImageLocation' => image_location
103
+ }
104
+
105
+ response = send_query_request(params)
106
+ parser = Awsum::Ec2::RegisterImageParser.new(self)
107
+ parser.parse(response.body)
108
+ end
109
+
110
+ # Deregister an Image. Once deregistered, you can no longer launch the Image
111
+ def deregister_image(image_id)
112
+ action = 'DeregisterImage'
113
+ params = {
114
+ 'Action' => action,
115
+ 'ImageId' => image_id
116
+ }
117
+
118
+ response = send_query_request(params)
119
+ response.is_a?(Net::HTTPSuccess)
120
+ end
121
+
122
+ # Launch an ec2 Instance
123
+ #
124
+ # ===Options:
125
+ # * <tt>:min</tt> - The minimum number of instances to launch. Default: 1
126
+ # * <tt>:max</tt> - The maximum number of instances to launch. Default: 1
127
+ # * <tt>:key_name</tt> - The name of the key pair with which to launch instances
128
+ # * <tt>:security_groups</tt> - The names of security groups to associate launched instances with
129
+ # * <tt>:user_data</tt> - User data made available to instances (Note: Must be 16K or less, will be base64 encoded by Awsum)
130
+ # * <tt>:instance_type</tt> - The size of the instances to launch, can be one of [m1.small, m1.large, m1.xlarge, c1.medium, c1.xlarge], default is m1.small
131
+ # * <tt>:availability_zone</tt> - The name of the availability zone to launch this Instance in
132
+ # * <tt>:kernel_id</tt> - The ID of the kernel with which to launch instances
133
+ # * <tt>:ramdisk_id</tt> - The ID of the RAM disk with which to launch instances
134
+ # * <tt>:block_device_map</tt> - A 'hash' of mappings. E.g. {'instancestore0' => 'sdb'}
135
+ def run_instances(image_id, options = {})
136
+ options = {:min => 1, :max => 1}.merge(options)
137
+ action = 'RunInstances'
138
+ params = {
139
+ 'Action' => action,
140
+ 'ImageId' => image_id,
141
+ 'MinCount' => options[:min],
142
+ 'MaxCount' => options[:max],
143
+ 'KeyName' => options[:key_name],
144
+ 'UserData' => options[:user_data].nil? ? nil : Base64::encode64(options[:user_data]).gsub(/\n/, ''),
145
+ 'InstanceType' => options[:instance_type],
146
+ 'Placement.AvailabilityZone' => options[:availability_zone],
147
+ 'KernelId' => options[:kernel_id],
148
+ 'RamdiskId' => options[:ramdisk_id]
149
+ }
150
+ if options[:block_device_map].respond_to?(:keys)
151
+ map = options[:block_device_map]
152
+ map.keys.each_with_index do |key, i|
153
+ params["BlockDeviceMapping.#{i+1}.VirtualName"] = key
154
+ params["BlockDeviceMapping.#{i+1}.DeviceName"] = map[key]
155
+ end
156
+ else
157
+ raise ArgumentError.new("options[:block_device_map] - must be a key => value map") unless options[:block_device_map].nil?
158
+ end
159
+ params.merge!(array_to_params(options[:security_groups], "SecurityGroup"))
160
+
161
+ response = send_query_request(params)
162
+ parser = Awsum::Ec2::InstanceParser.new(self)
163
+ parser.parse(response.body)
164
+ end
165
+ alias_method :launch_instances, :run_instances
166
+
167
+ #Retrieve the information on a number of Instance(s)
168
+ def instances(*instance_ids)
169
+ action = 'DescribeInstances'
170
+ params = {
171
+ 'Action' => action
172
+ }
173
+ params.merge!(array_to_params(instance_ids, 'InstanceId'))
174
+
175
+ response = send_query_request(params)
176
+ parser = Awsum::Ec2::InstanceParser.new(self)
177
+ parser.parse(response.body)
178
+ end
179
+
180
+ #Retrieve the information on a single Instance
181
+ def instance(instance_id)
182
+ instances([instance_id])[0]
183
+ end
184
+
185
+ # Retrieves the currently running Instance
186
+ # This should only be run on a running EC2 instance
187
+ def me
188
+ require 'open-uri'
189
+ begin
190
+ instance_id = open('http://169.254.169.254/latest/meta-data/instance-id').read
191
+ instance instance_id
192
+ rescue OpenURI::HTTPError => e
193
+ nil
194
+ end
195
+ end
196
+
197
+ # Retreives the user-data supplied when starting the currently running Instance
198
+ # This should only be run on a running EC2 instance
199
+ def user_data
200
+ require 'open-uri'
201
+ begin
202
+ open('http://169.254.169.254/latest/user-data').read
203
+ rescue OpenURI::HTTPError => e
204
+ nil
205
+ end
206
+ end
207
+
208
+ # Terminates the Instance(s)
209
+ #
210
+ # Returns true if the terminations succeeds, false otherwise
211
+ def terminate_instances(*instance_ids)
212
+ action = 'TerminateInstances'
213
+ params = {
214
+ 'Action' => action
215
+ }
216
+ params.merge!(array_to_params(instance_ids, 'InstanceId'))
217
+
218
+ response = send_query_request(params)
219
+ response.is_a?(Net::HTTPSuccess)
220
+ end
221
+
222
+ #Retrieve the information on a number of Volume(s)
223
+ def volumes(*volume_ids)
224
+ action = 'DescribeVolumes'
225
+ params = {
226
+ 'Action' => action
227
+ }
228
+ params.merge!(array_to_params(volume_ids, 'VolumeId'))
229
+
230
+ response = send_query_request(params)
231
+ parser = Awsum::Ec2::VolumeParser.new(self)
232
+ parser.parse(response.body)
233
+ end
234
+
235
+ # Retreive information on a Volume
236
+ def volume(volume_id)
237
+ volumes(volume_id)[0]
238
+ end
239
+
240
+ # Create a new volume
241
+ #
242
+ # ===Options:
243
+ # * <tt>:size</tt> - The size of the volume to be created (in GB) (<b>NOTE:</b> Required if you are not creating from a snapshot)
244
+ # * <tt>:snapshot_id</tt> - The snapshot id from which to create the volume
245
+ #
246
+ def create_volume(availability_zone, options = {})
247
+ raise ArgumentError.new('You must specify a size if not creating a volume from a snapshot') if options[:snapshot_id].blank? && options[:size].blank?
248
+
249
+ action = 'CreateVolume'
250
+ params = {
251
+ 'Action' => action,
252
+ 'AvailabilityZone' => availability_zone
253
+ }
254
+ params['Size'] = options[:size] unless options[:size].blank?
255
+ params['SnapshotId'] = options[:snapshot_id] unless options[:snapshot_id].blank?
256
+
257
+ response = send_query_request(params)
258
+ parser = Awsum::Ec2::VolumeParser.new(self)
259
+ parser.parse(response.body)[0]
260
+ end
261
+
262
+ # Attach a volume to an instance
263
+ def attach_volume(volume_id, instance_id, device = '/dev/sdh')
264
+ action = 'AttachVolume'
265
+ params = {
266
+ 'Action' => action,
267
+ 'VolumeId' => volume_id,
268
+ 'InstanceId' => instance_id,
269
+ 'Device' => device
270
+ }
271
+
272
+ response = send_query_request(params)
273
+ response.is_a?(Net::HTTPSuccess)
274
+ end
275
+
276
+ # Detach a volume from an instance
277
+ #
278
+ # ===Options
279
+ # * <tt>:instance_id</tt> - The ID of the instance from which the volume will detach
280
+ # * <tt>:device</tt> - The device name
281
+ # * <tt>:force</tt> - Whether to force the detachment. <b>NOTE:</b> If forced you may have data corruption issues.
282
+ def detach_volume(volume_id, options = {})
283
+ action = 'DetachVolume'
284
+ params = {
285
+ 'Action' => action,
286
+ 'VolumeId' => volume_id
287
+ }
288
+ params['InstanceId'] = options[:instance_id] unless options[:instance_id].blank?
289
+ params['Device'] = options[:device] unless options[:device].blank?
290
+ params['Force'] = options[:force] unless options[:force].blank?
291
+
292
+ response = send_query_request(params)
293
+ response.is_a?(Net::HTTPSuccess)
294
+ end
295
+
296
+ # Delete a volume
297
+ def delete_volume(volume_id)
298
+ action = 'DeleteVolume'
299
+ params = {
300
+ 'Action' => action,
301
+ 'VolumeId' => volume_id
302
+ }
303
+
304
+ response = send_query_request(params)
305
+ response.is_a?(Net::HTTPSuccess)
306
+ end
307
+
308
+ # Create a Snapshot of a Volume
309
+ def create_snapshot(volume_id)
310
+ action = 'CreateSnapshot'
311
+ params = {
312
+ 'Action' => action,
313
+ 'VolumeId' => volume_id
314
+ }
315
+
316
+ response = send_query_request(params)
317
+ parser = Awsum::Ec2::SnapshotParser.new(self)
318
+ parser.parse(response.body)[0]
319
+ end
320
+
321
+ # List Snapshot(s)
322
+ def snapshots(*snapshot_ids)
323
+ action = 'DescribeSnapshots'
324
+ params = {
325
+ 'Action' => action
326
+ }
327
+ params.merge!(array_to_params(snapshot_ids, 'SnapshotId'))
328
+
329
+ response = send_query_request(params)
330
+ parser = Awsum::Ec2::SnapshotParser.new(self)
331
+ parser.parse(response.body)
332
+ end
333
+
334
+ # Get the information about a Snapshot
335
+ def snapshot(snapshot_id)
336
+ snapshots(snapshot_id)[0]
337
+ end
338
+
339
+ # Delete a Snapshot
340
+ def delete_snapshot(snapshot_id)
341
+ action = 'DeleteSnapshot'
342
+ params = {
343
+ 'Action' => action,
344
+ 'SnapshotId' => snapshot_id
345
+ }
346
+
347
+ response = send_query_request(params)
348
+ response.is_a?(Net::HTTPSuccess)
349
+ end
350
+
351
+ # List all AvailabilityZone(s)
352
+ def availability_zones(*zone_names)
353
+ action = 'DescribeAvailabilityZones'
354
+ params = {
355
+ 'Action' => action
356
+ }
357
+ params.merge!(array_to_params(zone_names, 'ZoneName'))
358
+
359
+ response = send_query_request(params)
360
+ parser = Awsum::Ec2::AvailabilityZoneParser.new(self)
361
+ parser.parse(response.body)
362
+ end
363
+
364
+ # List all Region(s)
365
+ def regions(*region_names)
366
+ action = 'DescribeRegions'
367
+ params = {
368
+ 'Action' => action
369
+ }
370
+ params.merge!(array_to_params(region_names, 'RegionName'))
371
+
372
+ response = send_query_request(params)
373
+ parser = Awsum::Ec2::RegionParser.new(self)
374
+ parser.parse(response.body)
375
+ end
376
+
377
+ # List a Region
378
+ def region(region_name, &block)
379
+ region = regions(region_name)[0]
380
+ if block_given?
381
+ region.use(&block)
382
+ else
383
+ region
384
+ end
385
+ end
386
+
387
+ # List Addresses
388
+ def addresses(*public_ips)
389
+ action = 'DescribeAddresses'
390
+ params = {
391
+ 'Action' => action
392
+ }
393
+ params.merge!(array_to_params(public_ips, 'PublicIp'))
394
+
395
+ response = send_query_request(params)
396
+ parser = Awsum::Ec2::AddressParser.new(self)
397
+ parser.parse(response.body)
398
+ end
399
+
400
+ # Get the Address with a specific public ip
401
+ def address(public_ip)
402
+ addresses(public_ip)[0]
403
+ end
404
+
405
+ # Allocate Address
406
+ #
407
+ # Will aquire an elastic ip address for use with your account
408
+ def allocate_address
409
+ action = 'AllocateAddress'
410
+ params = {
411
+ 'Action' => action
412
+ }
413
+
414
+ response = send_query_request(params)
415
+ parser = Awsum::Ec2::AddressParser.new(self)
416
+ parser.parse(response.body)[0]
417
+ end
418
+
419
+ # Associate Address
420
+ #
421
+ # Will link an allocated elastic ip address to an Instance
422
+ #
423
+ # <b>NOTE:</b> If the ip address is already associated with another instance, it will be associated with the new instance.
424
+ #
425
+ # You can run this command more than once and it will not return an error.
426
+ def associate_address(instance_id, public_ip)
427
+ action = 'AssociateAddress'
428
+ params = {
429
+ 'Action' => action,
430
+ 'InstanceId' => instance_id,
431
+ 'PublicIp' => public_ip
432
+ }
433
+
434
+ response = send_query_request(params)
435
+ response.is_a?(Net::HTTPSuccess)
436
+ end
437
+
438
+ # Disassociate Address
439
+ #
440
+ # Will disassociate an allocated elastic ip address from the Instance it's allocated to
441
+ #
442
+ # <b>NOTE:</b> You can run this command more than once and it will not return an error.
443
+ def disassociate_address(public_ip)
444
+ action = 'DisassociateAddress'
445
+ params = {
446
+ 'Action' => action,
447
+ 'PublicIp' => public_ip
448
+ }
449
+
450
+ response = send_query_request(params)
451
+ response.is_a?(Net::HTTPSuccess)
452
+ end
453
+
454
+ # Releases an associated Address
455
+ #
456
+ # <b>NOTE:</b> This is not a direct call to the Amazon web service. This is a safe operation that will first check to see if the address is allocated to an instance and fail if it is
457
+ # To ensure an address is released whether associated or not, use #release_address!
458
+ def release_address(public_ip)
459
+ address = address(public_ip)
460
+
461
+ if address.instance_id.nil?
462
+ action = 'ReleaseAddress'
463
+ params = {
464
+ 'Action' => action,
465
+ 'PublicIp' => public_ip
466
+ }
467
+
468
+ response = send_query_request(params)
469
+ response.is_a?(Net::HTTPSuccess)
470
+ else
471
+ raise 'Address is currently allocated' #FIXME: Add a proper Awsum error here
472
+ end
473
+ end
474
+
475
+ # Releases an associated Address
476
+ #
477
+ # <b>NOTE:</b> This will disassociate an address automatically if it is associated with an instance
478
+ def release_address!(public_ip)
479
+ action = 'ReleaseAddress'
480
+ params = {
481
+ 'Action' => action,
482
+ 'PublicIp' => public_ip
483
+ }
484
+
485
+ response = send_query_request(params)
486
+ response.is_a?(Net::HTTPSuccess)
487
+ end
488
+
489
+ # List KeyPair(s)
490
+ def key_pairs(*key_names)
491
+ action = 'DescribeKeyPairs'
492
+ params = {
493
+ 'Action' => action
494
+ }
495
+ params.merge!(array_to_params(key_names, 'KeyName'))
496
+
497
+ response = send_query_request(params)
498
+ parser = Awsum::Ec2::KeyPairParser.new(self)
499
+ parser.parse(response.body)
500
+ end
501
+
502
+ # Get a single KeyPair
503
+ def key_pair(key_name)
504
+ key_pairs(key_name)[0]
505
+ end
506
+
507
+ # Create a new KeyPair
508
+ def create_key_pair(key_name)
509
+ action = 'CreateKeyPair'
510
+ params = {
511
+ 'Action' => action,
512
+ 'KeyName' => key_name
513
+ }
514
+
515
+ response = send_query_request(params)
516
+ parser = Awsum::Ec2::KeyPairParser.new(self)
517
+ parser.parse(response.body)[0]
518
+ end
519
+
520
+ # Delete a KeyPair
521
+ def delete_key_pair(key_name)
522
+ action = 'DeleteKeyPair'
523
+ params = {
524
+ 'Action' => action,
525
+ 'KeyName' => key_name
526
+ }
527
+
528
+ response = send_query_request(params)
529
+ response.is_a?(Net::HTTPSuccess)
530
+ end
531
+
532
+ # List SecurityGroup(s)
533
+ def security_groups(*group_names)
534
+ action = 'DescribeSecurityGroups'
535
+ params = {
536
+ 'Action' => action
537
+ }
538
+ params.merge!(array_to_params(group_names, 'GroupName'))
539
+
540
+ response = send_query_request(params)
541
+ parser = Awsum::Ec2::SecurityGroupParser.new(self)
542
+ parser.parse(response.body)
543
+ end
544
+
545
+ # Get a single SecurityGroup
546
+ def security_group(group_name)
547
+ security_groups(group_name)[0]
548
+ end
549
+
550
+ # Create a new SecurityGroup
551
+ def create_security_group(name, description)
552
+ action = 'CreateSecurityGroup'
553
+ params = {
554
+ 'Action' => action,
555
+ 'GroupName' => name,
556
+ 'GroupDescription' => description
557
+ }
558
+
559
+ response = send_query_request(params)
560
+ response.is_a?(Net::HTTPSuccess)
561
+ end
562
+
563
+ # Delete a SecurityGroup
564
+ def delete_security_group(group_name)
565
+ action = 'DeleteSecurityGroup'
566
+ params = {
567
+ 'Action' => action,
568
+ 'GroupName' => group_name
569
+ }
570
+
571
+ response = send_query_request(params)
572
+ response.is_a?(Net::HTTPSuccess)
573
+ end
574
+
575
+ # Authorize access on a specific security group
576
+ #
577
+ # ===Options:
578
+ # ====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
581
+ # ====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] }
598
+
599
+ action = 'AuthorizeSecurityGroupIngress'
600
+ params = {
601
+ 'Action' => action,
602
+ 'GroupName' => group_name
603
+ }
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?
610
+
611
+ response = send_query_request(params)
612
+ response.is_a?(Net::HTTPSuccess)
613
+ end
614
+
615
+ # Revoke access on a specific SecurityGroup
616
+ #
617
+ # ===Options:
618
+ # ====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
621
+ # ====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] }
638
+
639
+ action = 'RevokeSecurityGroupIngress'
640
+ params = {
641
+ 'Action' => action,
642
+ 'GroupName' => group_name
643
+ }
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?
650
+
651
+ response = send_query_request(params)
652
+ response.is_a?(Net::HTTPSuccess)
653
+ end
654
+
655
+ # List all reserved instance offerings that are available for purchase
656
+ #
657
+ # ===Options:
658
+ # * <tt>:reserved_instances_offering_ids</tt> - Display the reserved instance offerings with the specified ids. Can be an individual id or an array of ids
659
+ # * <tt>:instance_type</tt> - Display available reserved instance offerings of the specific instance type, can be one of [m1.small, m1.large, m1.xlarge, c1.medium, c1.xlarge], default is all
660
+ # * <tt>:availability_zone</tt> - Display the reserved instance offerings within the specified availability zone
661
+ # * <tt>:product_description</tt> - Display the reserved instance offerings with the specified product description
662
+ #
663
+ # ===Example
664
+ # #To get all offerings for m1.small instances in availability zone us-east-1a
665
+ # ec2.reserved_instances_offerings(:instance_type => 'm1.small', :availability_zone => 'us-east-1a')
666
+ def reserved_instances_offerings(options = {})
667
+ action = 'DescribeReservedInstancesOfferings'
668
+ params = {
669
+ 'Action' => action
670
+ }
671
+ params.merge!(array_to_params(options[:reserved_instances_offering_ids], 'ReservedInstancesOfferingId')) if options[:reserved_instances_offering_ids]
672
+ params['InstanceType'] = options[:instance_type] if options[:instance_type]
673
+ params['AvailabilityZone'] = options[:availability_zone] if options[:availability_zone]
674
+ params['ProductDescription'] = options[:product_description] if options[:product_description]
675
+
676
+ response = send_query_request(params)
677
+ parser = Awsum::Ec2::ReservedInstancesOfferingParser.new(self)
678
+ parser.parse(response.body)
679
+ end
680
+
681
+ # Get a single reserved instances offering by id
682
+ def reserved_instances_offering(id)
683
+ reserved_instances_offerings(:reserved_instances_offering_ids => id)[0]
684
+ end
685
+
686
+ # Purchase reserved instances
687
+ #
688
+ # ===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)
691
+ #
692
+ # ===Example
693
+ # 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)
697
+ action = 'PurchaseReservedInstancesOffering'
698
+ params = {
699
+ 'Action' => action,
700
+ }
701
+ params.merge!(array_to_params([instance_counts].flatten, 'InstanceCount'))
702
+ params.merge!(array_to_params([reserved_instances_offering_ids].flatten, 'ReservedInstancesOfferingId'))
703
+
704
+ response = send_query_request(params)
705
+ parser = Awsum::Ec2::PurchaseReservedInstancesOfferingParser.new(self)
706
+ 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
+ end
713
+
714
+ def reserved_instances(*reserved_instances_ids)
715
+ action = 'DescribeReservedInstances'
716
+ params = {
717
+ 'Action' => action
718
+ }
719
+ params.merge!(array_to_params(reserved_instances_ids, 'ReservedInstanceId'))
720
+
721
+ response = send_query_request(params)
722
+ parser = Awsum::Ec2::ReservedInstanceParser.new(self)
723
+ parser.parse(response.body)
724
+ end
725
+
726
+ # Retrieve a single reserved instance by id
727
+ def reserved_instance(reserved_instance_id)
728
+ reserved_instances(reserved_instance_id)[0]
729
+ end
730
+
731
+ #private
732
+ #The host to make all requests against
733
+ def host
734
+ @host ||= 'ec2.amazonaws.com'
735
+ end
736
+
737
+ def host=(host)
738
+ @host = host
739
+ end
740
+ end
741
+ end