skippy-amazon-ec2 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+
6
+ # read the contents of the gemspec, eval it, and assign it to 'spec'
7
+ # this lets us maintain all gemspec info in one place. Nice and DRY.
8
+ spec = eval(IO.read("amazon-ec2.gemspec"))
9
+
10
+ Rake::GemPackageTask.new(spec) do |pkg|
11
+ pkg.gem_spec = spec
12
+ end
13
+
14
+ task :install => [:package] do
15
+ sh %{sudo gem install pkg/#{GEM}-#{VERSION}}
16
+ end
17
+
18
+ Rake::TestTask.new do |t|
19
+ t.libs << "test"
20
+ t.test_files = FileList['test/test*.rb']
21
+ t.verbose = true
22
+ end
23
+
24
+ Rake::RDocTask.new do |rd|
25
+ rd.main = "README.rdoc"
26
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
27
+ rd.rdoc_dir = 'doc'
28
+ rd.options = spec.rdoc_options
29
+ end
30
+
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Amazon Web Services EC2 Query API Ruby library
4
+ #
5
+ # Ruby Gem Name:: amazon-ec2
6
+ # Author:: Glenn Rempe (mailto:grempe@rubyforge.org)
7
+ # Copyright:: Copyright (c) 2007-2008 Glenn Rempe
8
+ # License:: Distributes under the same terms as Ruby
9
+ # Home:: http://amazon-ec2.rubyforge.org
10
+ #++
11
+
12
+ require 'rubygems'
13
+ require File.dirname(__FILE__) + '/../lib/EC2'
14
+
15
+ # pull these from the local shell environment variables set in ~/.bash_login
16
+ # or using appropriate methods specific to your login shell.
17
+ #
18
+ # e.g. in ~/.bash_login
19
+ #
20
+ # # For amazon-ec2 and amazon s3 ruby gems
21
+ # export AMAZON_ACCESS_KEY_ID="FOO"
22
+ # export AMAZON_SECRET_ACCESS_KEY="BAR"
23
+
24
+ ACCESS_KEY_ID = ENV['AMAZON_ACCESS_KEY_ID']
25
+ SECRET_ACCESS_KEY = ENV['AMAZON_SECRET_ACCESS_KEY']
26
+
27
+ if ACCESS_KEY_ID.nil? || ACCESS_KEY_ID.empty?
28
+ puts "Error : You must add the shell environment variables AMAZON_ACCESS_KEY_ID and AMAZON_SECRET_ACCESS_KEY before calling #{$0}!"
29
+ exit
30
+ end
31
+
32
+ ec2 = EC2::Base.new( :access_key_id => ACCESS_KEY_ID, :secret_access_key => SECRET_ACCESS_KEY )
33
+
34
+ puts "----- ec2.methods.sort -----"
35
+ p ec2.methods.sort
36
+
37
+ puts "----- listing images owned by 'amazon' -----"
38
+ ec2.describe_images(:owner_id => "amazon").imagesSet.item.each do |image|
39
+ image.members.each do |member|
40
+ puts "#{member} => #{image[member]}"
41
+ end
42
+ end
43
+
44
+ puts "----- listing all running instances -----"
45
+ puts ec2.describe_instances()
46
+
47
+ puts "----- creating a security group -----"
48
+ puts ec2.create_security_group(:group_name => "ec2-example-rb-test-group", :group_description => "ec-example.rb test group description.")
49
+
50
+ puts "----- listing security groups -----"
51
+ puts ec2.describe_security_groups()
52
+
53
+ puts "----- deleting a security group -----"
54
+ puts ec2.delete_security_group(:group_name => "ec2-example-rb-test-group")
55
+
56
+ puts "----- listing my keypairs (verbose mode) -----"
57
+ puts ec2.describe_keypairs()
58
+
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Amazon Web Services EC2 Query API Ruby library
4
+ #
5
+ # Ruby Gem Name:: amazon-ec2
6
+ # Author:: Glenn Rempe (mailto:grempe@rubyforge.org)
7
+ # Copyright:: Copyright (c) 2007-2008 Glenn Rempe
8
+ # License:: Distributes under the same terms as Ruby
9
+ # Home:: http://amazon-ec2.rubyforge.org
10
+ #++
11
+
12
+ # CREDITS : Credit for this bit of shameful ripoff coolness
13
+ # goes to Marcel Molina and his AWS::S3 gem. Thanks!
14
+
15
+ # Usage : running this starts up an irb session and
16
+ # sets up the connection to EC2 as a class variable called
17
+ # '@ec2'. So just do something like the following on the
18
+ # shell command line:
19
+
20
+ # macbook-pro:~ glenn$ ec2sh
21
+ # >> @ec2.describe_images
22
+ # => [#<EC2::Item image_location...
23
+
24
+ ec2_lib = File.dirname(__FILE__) + '/../lib/EC2'
25
+ setup = File.dirname(__FILE__) + '/setup'
26
+ irb_name = RUBY_PLATFORM =~ /mswin32/ ? 'irb.bat' : 'irb'
27
+
28
+ if ( ENV['AMAZON_ACCESS_KEY_ID'] && ENV['AMAZON_SECRET_ACCESS_KEY'] )
29
+
30
+ welcome_message = <<-MESSAGE
31
+
32
+ 'ec2sh' usage :
33
+ This is an interactive 'irb' command shell that allows you to use all
34
+ commands available to the amazon-ec2 gem. You'll find this to be a
35
+ great tool to help you debug issues and practice running commands
36
+ against the live EC2 servers prior to putting them in your code.
37
+
38
+ The EC2 connection is wired to the class instance '@ec2'. Make method calls
39
+ on this to execute commands on EC2. Adding a #to_s
40
+ at the end of any command should give you a full String representation of the
41
+ response. The #xml data is available for each response
42
+ which allows you to view the full and complete XML response returned by
43
+ EC2 without any parsing applied. This is useful for viewing the
44
+ hierarchy of an entire response in a friendly way (if XML is friendly
45
+ to you!). Understanding the hierarchy of the XML response is critical
46
+ to making effective use of this library.
47
+
48
+ Examples to try:
49
+
50
+ returns : all ec2 public methods
51
+ >> @ec2.methods.sort
52
+
53
+ returns : a string representation of ALL images
54
+ >> @ec2.describe_images.to_s
55
+
56
+ returns : an Array of EC2::Response objects, each an EC2 image and its data
57
+ >> @ec2.describe_images.imagesSet.item
58
+ >> @ec2.describe_images.imagesSet.item[0] (an OpenStruct of a single item in that array)
59
+ >> @ec2.describe_images.imagesSet.item[0].to_s (a String representation of that OpenStruct item)
60
+
61
+ returns : an XML representation of all images
62
+ >> puts @ec2.describe_images.xml
63
+
64
+ returns : an XML representation of all images owned by Amazon
65
+ >> puts @ec2.describe_images(:owner_id => 'amazon').xml
66
+
67
+ MESSAGE
68
+
69
+ puts welcome_message
70
+ exec "#{irb_name} -r #{ec2_lib} -r #{setup} --simple-prompt"
71
+ else
72
+ puts "You must define AMAZON_ACCESS_KEY_ID and AMAZON_SECRET_ACCESS_KEY as shell environment variables before running #{$0}!"
73
+ end
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Amazon Web Services EC2 Query API Ruby library
4
+ #
5
+ # Ruby Gem Name:: amazon-ec2
6
+ # Author:: Glenn Rempe (mailto:grempe@rubyforge.org)
7
+ # Copyright:: Copyright (c) 2007-2008 Glenn Rempe
8
+ # License:: Distributes under the same terms as Ruby
9
+ # Home:: http://amazon-ec2.rubyforge.org
10
+ #++
11
+
12
+ if ENV['AMAZON_ACCESS_KEY_ID'] && ENV['AMAZON_SECRET_ACCESS_KEY']
13
+ @ec2 = EC2::Base.new(
14
+ :access_key_id => ENV['AMAZON_ACCESS_KEY_ID'],
15
+ :secret_access_key => ENV['AMAZON_SECRET_ACCESS_KEY']
16
+ )
17
+ end
18
+
19
+ include EC2
@@ -0,0 +1,247 @@
1
+ #--
2
+ # Amazon Web Services EC2 Query API Ruby library
3
+ #
4
+ # Ruby Gem Name:: amazon-ec2
5
+ # Author:: Glenn Rempe (mailto:grempe@rubyforge.org)
6
+ # Copyright:: Copyright (c) 2007-2008 Glenn Rempe
7
+ # License:: Distributes under the same terms as Ruby
8
+ # Home:: http://amazon-ec2.rubyforge.org
9
+ #++
10
+
11
+ %w[ base64 cgi openssl digest/sha1 net/https rexml/document time ostruct ].each { |f| require f }
12
+
13
+ # Require any lib files that we have bundled with this Ruby Gem in the lib/EC2 directory.
14
+ # Parts of the EC2 module and Base class are broken out into separate
15
+ # files for maintainability and are organized by the functional groupings defined
16
+ # in the EC2 API developers guide.
17
+ Dir[File.join(File.dirname(__FILE__), 'EC2/**/*.rb')].sort.each { |lib| require lib }
18
+
19
+ module EC2
20
+
21
+ # Which host FQDN will we connect to for all API calls to AWS?
22
+ DEFAULT_HOST = 'ec2.amazonaws.com'
23
+
24
+ # This is the version of the API as defined by Amazon Web Services
25
+ API_VERSION = '2008-05-05'
26
+
27
+ # Builds the canonical string for signing. This strips out all '&', '?', and '='
28
+ # from the query string to be signed.
29
+ # Note: The parameters in the path passed in must already be sorted in
30
+ # case-insensitive alphabetical order and must not be url encoded.
31
+ def EC2.canonical_string(path)
32
+ buf = ""
33
+ path.split('&').each { |field|
34
+ buf << field.gsub(/\&|\?/,"").sub(/=/,"")
35
+ }
36
+ return buf
37
+ end
38
+
39
+ # Encodes the given string with the secret_access_key, by taking the
40
+ # hmac-sha1 sum, and then base64 encoding it. Optionally, it will also
41
+ # url encode the result of that to protect the string if it's going to
42
+ # be used as a query string parameter.
43
+ def EC2.encode(secret_access_key, str, urlencode=true)
44
+ digest = OpenSSL::Digest::Digest.new('sha1')
45
+ b64_hmac =
46
+ Base64.encode64(
47
+ OpenSSL::HMAC.digest(digest, secret_access_key, str)).strip
48
+
49
+ if urlencode
50
+ return CGI::escape(b64_hmac)
51
+ else
52
+ return b64_hmac
53
+ end
54
+ end
55
+
56
+
57
+ #Introduction:
58
+ #
59
+ # The library exposes one main interface class, 'EC2::Base'.
60
+ # This class provides all the methods for using the EC2 service
61
+ # including the handling of header signing and other security issues .
62
+ # This class uses Net::HTTP to interface with the EC2 Query API interface.
63
+ #
64
+ #Required Arguments:
65
+ #
66
+ # :access_key_id => String (default : "")
67
+ # :secret_access_key => String (default : "")
68
+ #
69
+ #Optional Arguments:
70
+ #
71
+ # :use_ssl => Boolean (default : true)
72
+ # :server => String (default : 'ec2.amazonaws.com')
73
+ # :proxy_server => String (default : nil)
74
+ #
75
+ class Base
76
+
77
+ attr_reader :use_ssl, :server, :proxy_server, :port
78
+
79
+ def initialize( options = {} )
80
+
81
+ options = { :access_key_id => "",
82
+ :secret_access_key => "",
83
+ :use_ssl => true,
84
+ :server => DEFAULT_HOST,
85
+ :proxy_server => nil
86
+ }.merge(options)
87
+
88
+ @server = options[:server]
89
+ @proxy_server = options[:proxy_server]
90
+ @use_ssl = options[:use_ssl]
91
+
92
+ raise ArgumentError, "No :access_key_id provided" if options[:access_key_id].nil? || options[:access_key_id].empty?
93
+ raise ArgumentError, "No :secret_access_key provided" if options[:secret_access_key].nil? || options[:secret_access_key].empty?
94
+ raise ArgumentError, "No :use_ssl value provided" if options[:use_ssl].nil?
95
+ raise ArgumentError, "Invalid :use_ssl value provided, only 'true' or 'false' allowed" unless options[:use_ssl] == true || options[:use_ssl] == false
96
+ raise ArgumentError, "No :server provided" if options[:server].nil? || options[:server].empty?
97
+
98
+
99
+ # based on the :use_ssl boolean, determine which port we should connect to
100
+ case @use_ssl
101
+ when true
102
+ # https
103
+ @port = 443
104
+ when false
105
+ # http
106
+ @port = 80
107
+ end
108
+
109
+ @access_key_id = options[:access_key_id]
110
+ @secret_access_key = options[:secret_access_key]
111
+
112
+ # Use proxy server if defined
113
+ # Based on patch by Mathias Dalheimer. 20070217
114
+ proxy = @proxy_server ? URI.parse(@proxy_server) : OpenStruct.new
115
+ @http = Net::HTTP::Proxy( proxy.host,
116
+ proxy.port,
117
+ proxy.user,
118
+ proxy.password).new(options[:server], @port)
119
+
120
+ @http.use_ssl = @use_ssl
121
+
122
+ # Don't verify the SSL certificates. Avoids SSL Cert warning in log on every GET.
123
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
124
+
125
+ end
126
+
127
+
128
+ private
129
+
130
+ # pathlist is a utility method which takes a key string and and array as input.
131
+ # It converts the array into a Hash with the hash key being 'Key.n' where
132
+ # 'n' increments by 1 for each iteration. So if you pass in args
133
+ # ("ImageId", ["123", "456"]) you should get
134
+ # {"ImageId.1"=>"123", "ImageId.2"=>"456"} returned.
135
+ def pathlist(key, arr)
136
+ params = {}
137
+ arr.each_with_index do |value, i|
138
+ params["#{key}.#{i+1}"] = value
139
+ end
140
+ params
141
+ end
142
+
143
+
144
+ # Make the connection to AWS EC2 passing in our request. This is generally called from
145
+ # within a 'Response' class object or one of its sub-classes so the response is interpreted
146
+ # in its proper context. See lib/EC2/responses.rb
147
+ def make_request(action, params, data='')
148
+
149
+ @http.start do
150
+
151
+ # remove any keys that have nil or empty values
152
+ params.reject! { |key, value| value.nil? or value.empty?}
153
+
154
+ params.merge!( {"Action" => action,
155
+ "SignatureVersion" => "1",
156
+ "AWSAccessKeyId" => @access_key_id,
157
+ "Version" => API_VERSION,
158
+ "Timestamp"=>Time.now.getutc.iso8601} )
159
+
160
+ sigquery = params.sort_by { |param| param[0].downcase }.collect { |param| param.join("=") }.join("&")
161
+
162
+ sig = get_aws_auth_param(sigquery, @secret_access_key)
163
+
164
+ query = params.sort.collect do |param|
165
+ CGI::escape(param[0]) + "=" + CGI::escape(param[1])
166
+ end.join("&") + "&Signature=" + sig
167
+
168
+ req = Net::HTTP::Post.new("/")
169
+ req.content_type = 'application/x-www-form-urlencoded'
170
+ req['User-Agent'] = "github-amazon-ec2-ruby-gem"
171
+
172
+ response = @http.request(req, query)
173
+
174
+ # Make a call to see if we need to throw an error based on the response given by EC2
175
+ # All error classes are defined in EC2/exceptions.rb
176
+ ec2_error?(response)
177
+
178
+ return response
179
+
180
+ end
181
+
182
+ end
183
+
184
+ # Set the Authorization header using AWS signed header authentication
185
+ def get_aws_auth_param(path, secret_access_key)
186
+ canonical_string = EC2.canonical_string(path)
187
+ encoded_canonical = EC2.encode(secret_access_key, canonical_string)
188
+ end
189
+
190
+ # allow us to have a one line call in each method which will do all of the work
191
+ # in making the actual request to AWS.
192
+ def response_generator( options = {} )
193
+
194
+ options = {
195
+ :action => "",
196
+ :params => {}
197
+ }.merge(options)
198
+
199
+ raise ArgumentError, ":action must be provided to response_generator" if options[:action].nil? || options[:action].empty?
200
+
201
+ http_response = make_request(options[:action], options[:params])
202
+ http_xml = http_response.body
203
+ return Response.parse(:xml => http_xml)
204
+
205
+ end
206
+
207
+ # Raises the appropriate error if the specified Net::HTTPResponse object
208
+ # contains an Amazon EC2 error; returns +false+ otherwise.
209
+ def ec2_error?(response)
210
+
211
+ # return false if we got a HTTP 200 code,
212
+ # otherwise there is some type of error (40x,50x) and
213
+ # we should try to raise an appropriate exception
214
+ # from one of our exception classes defined in
215
+ # exceptions.rb
216
+ return false if response.is_a?(Net::HTTPSuccess)
217
+
218
+ # parse the XML document so we can walk through it
219
+ doc = REXML::Document.new(response.body)
220
+
221
+ # Check that the Error element is in the place we would expect.
222
+ # and if not raise a generic error exception
223
+ unless doc.root.elements['Errors'].elements['Error'].name == 'Error'
224
+ raise Error, "Unexpected error format. response.body is: #{response.body}"
225
+ end
226
+
227
+ # An valid error response looks like this:
228
+ # <?xml version="1.0"?><Response><Errors><Error><Code>InvalidParameterCombination</Code><Message>Unknown parameter: foo</Message></Error></Errors><RequestID>291cef62-3e86-414b-900e-17246eccfae8</RequestID></Response>
229
+ # AWS EC2 throws some exception codes that look like Error.SubError. Since we can't name classes this way
230
+ # we need to strip out the '.' in the error 'Code' and we name the error exceptions with this
231
+ # non '.' name as well.
232
+ error_code = doc.root.elements['Errors'].elements['Error'].elements['Code'].text.gsub('.', '')
233
+ error_message = doc.root.elements['Errors'].elements['Error'].elements['Message'].text
234
+
235
+ # Raise one of our specific error classes if it exists.
236
+ # otherwise, throw a generic EC2 Error with a few details.
237
+ if EC2.const_defined?(error_code)
238
+ raise EC2.const_get(error_code), error_message
239
+ else
240
+ raise Error, "This is an undefined error code which needs to be added to exceptions.rb : error_code => #{error_code} : error_message => #{error_message}"
241
+ end
242
+
243
+ end
244
+
245
+ end
246
+
247
+ end
@@ -0,0 +1,42 @@
1
+ #--
2
+ # Amazon Web Services EC2 Query API Ruby library
3
+ #
4
+ # Ruby Gem Name:: amazon-ec2
5
+ # Author:: Glenn Rempe (mailto:grempe@rubyforge.org)
6
+ # Copyright:: Copyright (c) 2007-2008 Glenn Rempe
7
+ # License:: Distributes under the same terms as Ruby
8
+ # Home:: http://amazon-ec2.rubyforge.org
9
+ #++
10
+
11
+ module EC2
12
+
13
+ class Base
14
+
15
+ #Amazon Developer Guide Docs:
16
+ #
17
+ # The DescribeAvailabilityZones operation describes availability zones that are currently
18
+ # available to the account and their states.
19
+ #
20
+ # An optional list of zone names can be passed.
21
+ #
22
+ #Required Arguments:
23
+ #
24
+ # none
25
+ #
26
+ #Optional Arguments:
27
+ #
28
+ # :zone_name => Array (default : [])
29
+ #
30
+
31
+ def describe_availability_zones( options = {} )
32
+
33
+ options = { :zone_name => [] }.merge(options)
34
+
35
+ params = pathlist("ZoneName", options[:zone_name] )
36
+
37
+ return response_generator(:action => "DescribeAvailabilityZones", :params => params)
38
+
39
+ end
40
+ end
41
+ end
42
+