kunley-amazon-ec2 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -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
+ desc "Package and then install the gem locally"
15
+ task :install => [:package] do
16
+ sh %{sudo gem install pkg/#{GEM}-#{VER}}
17
+ end
18
+
19
+ Rake::TestTask.new do |t|
20
+ t.libs << "test"
21
+ t.test_files = FileList['test/test*.rb']
22
+ t.verbose = true
23
+ end
24
+
25
+ Rake::RDocTask.new do |rd|
26
+ rd.main = "README.rdoc"
27
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
28
+ rd.rdoc_dir = 'doc'
29
+ rd.options = spec.rdoc_options
30
+ end
@@ -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:glenn@rempe.us)
7
+ # Copyright:: Copyright (c) 2007-2008 Glenn Rempe
8
+ # License:: Distributes under the same terms as Ruby
9
+ # Home:: http://github.com/grempe/amazon-ec2/tree/master
10
+ #++
11
+
12
+ require 'rubygems'
13
+ require File.dirname(__FILE__) + '/../lib/EC2'
14
+ require 'pp'
15
+
16
+ # pull these from the local shell environment variables set in ~/.bash_login
17
+ # or using appropriate methods specific to your login shell.
18
+ #
19
+ # e.g. in ~/.bash_login
20
+ #
21
+ # # For amazon-ec2 and amazon s3 ruby gems
22
+ # export AMAZON_ACCESS_KEY_ID="FOO"
23
+ # export AMAZON_SECRET_ACCESS_KEY="BAR"
24
+
25
+ ACCESS_KEY_ID = ENV['AMAZON_ACCESS_KEY_ID']
26
+ SECRET_ACCESS_KEY = ENV['AMAZON_SECRET_ACCESS_KEY']
27
+
28
+ if ACCESS_KEY_ID.nil? || ACCESS_KEY_ID.empty?
29
+ puts "Error : You must add the shell environment variables AMAZON_ACCESS_KEY_ID and AMAZON_SECRET_ACCESS_KEY before calling #{$0}!"
30
+ exit
31
+ end
32
+
33
+ ec2 = EC2::Base.new( :access_key_id => ACCESS_KEY_ID, :secret_access_key => SECRET_ACCESS_KEY )
34
+
35
+ puts "----- ec2.methods.sort -----"
36
+ p ec2.methods.sort
37
+
38
+ puts "----- listing images owned by 'amazon' -----"
39
+ ec2.describe_images(:owner_id => "amazon").imagesSet.item.each do |image|
40
+ image.keys.each do |key|
41
+ puts "#{key} => #{image[key]}"
42
+ end
43
+ end
44
+
45
+ puts "----- listing all running instances -----"
46
+ pp ec2.describe_instances()
47
+
48
+ puts "----- creating a security group -----"
49
+ pp ec2.create_security_group(:group_name => "ec2-example-rb-test-group", :group_description => "ec-example.rb test group description.")
50
+
51
+ puts "----- listing security groups -----"
52
+ pp ec2.describe_security_groups()
53
+
54
+ puts "----- deleting a security group -----"
55
+ pp ec2.delete_security_group(:group_name => "ec2-example-rb-test-group")
56
+
57
+ puts "----- listing my keypairs (verbose mode) -----"
58
+ pp ec2.describe_keypairs()
data/bin/ec2sh ADDED
@@ -0,0 +1,62 @@
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:glenn@rempe.us)
7
+ # Copyright:: Copyright (c) 2007-2008 Glenn Rempe
8
+ # License:: Distributes under the same terms as Ruby
9
+ # Home:: http://github.com/grempe/amazon-ec2/tree/master
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.
42
+
43
+ Examples to try:
44
+
45
+ returns : all ec2 public methods
46
+ >> @ec2.methods.sort
47
+
48
+ returns : a string representation of ALL images
49
+ >> @ec2.describe_images.to_s
50
+
51
+ returns : an Array of EC2::Response objects, each an EC2 image and its data
52
+ >> @ec2.describe_images.imagesSet.item
53
+ >> @ec2.describe_images.imagesSet.item[0] (an OpenStruct of a single item in that array)
54
+ >> @ec2.describe_images.imagesSet.item[0].to_s (a String representation of that OpenStruct item)
55
+
56
+ MESSAGE
57
+
58
+ puts welcome_message
59
+ exec "#{irb_name} -r #{ec2_lib} -r #{setup} --simple-prompt"
60
+ else
61
+ puts "You must define AMAZON_ACCESS_KEY_ID and AMAZON_SECRET_ACCESS_KEY as shell environment variables before running #{$0}!"
62
+ end
data/bin/setup.rb ADDED
@@ -0,0 +1,26 @@
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:glenn@rempe.us)
7
+ # Copyright:: Copyright (c) 2007-2008 Glenn Rempe
8
+ # License:: Distributes under the same terms as Ruby
9
+ # Home:: http://github.com/grempe/amazon-ec2/tree/master
10
+ #++
11
+
12
+ if ENV['AMAZON_ACCESS_KEY_ID'] && ENV['AMAZON_SECRET_ACCESS_KEY']
13
+ opts = {
14
+ :access_key_id => ENV['AMAZON_ACCESS_KEY_ID'],
15
+ :secret_access_key => ENV['AMAZON_SECRET_ACCESS_KEY']
16
+ }
17
+ if ENV['EC2_URL']
18
+ opts[:server] = URI.parse(ENV['EC2_URL']).host
19
+ end
20
+ @ec2 = EC2::Base.new(opts)
21
+ end
22
+
23
+ puts "Current Options:"
24
+ pp opts
25
+
26
+ include EC2
data/lib/EC2.rb ADDED
@@ -0,0 +1,258 @@
1
+ #--
2
+ # Amazon Web Services EC2 Query API Ruby library
3
+ #
4
+ # Ruby Gem Name:: amazon-ec2
5
+ # Author:: Glenn Rempe (mailto:glenn@rempe.us)
6
+ # Copyright:: Copyright (c) 2007-2008 Glenn Rempe
7
+ # License:: Distributes under the same terms as Ruby
8
+ # Home:: http://github.com/grempe/amazon-ec2/tree/master
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-12-01'
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(params, host = DEFAULT_HOST, method="POST", base="/")
32
+ # Sort, and encode parameters into a canonical string.
33
+ sorted_params = params.sort {|x,y| x[0] <=> y[0]}
34
+ encoded_params = sorted_params.collect do |p|
35
+ encoded = (CGI::escape(p[0].to_s) +
36
+ "=" + CGI::escape(p[1].to_s))
37
+ # Ensure spaces are encoded as '%20', not '+'
38
+ encoded.gsub('+', '%20')
39
+ end
40
+ sigquery = encoded_params.join("&")
41
+
42
+ # Generate the request description string
43
+ req_desc =
44
+ method + "\n" +
45
+ host + "\n" +
46
+ base + "\n" +
47
+ sigquery
48
+
49
+ end
50
+
51
+ # Encodes the given string with the secret_access_key, by taking the
52
+ # hmac-sha1 sum, and then base64 encoding it. Optionally, it will also
53
+ # url encode the result of that to protect the string if it's going to
54
+ # be used as a query string parameter.
55
+ def EC2.encode(secret_access_key, str, urlencode=true)
56
+ digest = OpenSSL::Digest::Digest.new('sha1')
57
+ b64_hmac =
58
+ Base64.encode64(
59
+ OpenSSL::HMAC.digest(digest, secret_access_key, str)).gsub("\n","")
60
+
61
+ if urlencode
62
+ return CGI::escape(b64_hmac)
63
+ else
64
+ return b64_hmac
65
+ end
66
+ end
67
+
68
+
69
+ #Introduction:
70
+ #
71
+ # The library exposes one main interface class, 'EC2::Base'.
72
+ # This class provides all the methods for using the EC2 service
73
+ # including the handling of header signing and other security issues .
74
+ # This class uses Net::HTTP to interface with the EC2 Query API interface.
75
+ #
76
+ #Required Arguments:
77
+ #
78
+ # :access_key_id => String (default : "")
79
+ # :secret_access_key => String (default : "")
80
+ #
81
+ #Optional Arguments:
82
+ #
83
+ # :use_ssl => Boolean (default : true)
84
+ # :server => String (default : 'ec2.amazonaws.com')
85
+ # :proxy_server => String (default : nil)
86
+ #
87
+ class Base
88
+
89
+ attr_reader :use_ssl, :server, :proxy_server, :port
90
+
91
+ def initialize( options = {} )
92
+
93
+ options = { :access_key_id => "",
94
+ :secret_access_key => "",
95
+ :use_ssl => true,
96
+ :server => DEFAULT_HOST,
97
+ :proxy_server => nil
98
+ }.merge(options)
99
+
100
+ @server = options[:server]
101
+ @proxy_server = options[:proxy_server]
102
+ @use_ssl = options[:use_ssl]
103
+
104
+ raise ArgumentError, "No :access_key_id provided" if options[:access_key_id].nil? || options[:access_key_id].empty?
105
+ raise ArgumentError, "No :secret_access_key provided" if options[:secret_access_key].nil? || options[:secret_access_key].empty?
106
+ raise ArgumentError, "No :use_ssl value provided" if options[:use_ssl].nil?
107
+ raise ArgumentError, "Invalid :use_ssl value provided, only 'true' or 'false' allowed" unless options[:use_ssl] == true || options[:use_ssl] == false
108
+ raise ArgumentError, "No :server provided" if options[:server].nil? || options[:server].empty?
109
+
110
+
111
+ # based on the :use_ssl boolean, determine which port we should connect to
112
+ case @use_ssl
113
+ when true
114
+ # https
115
+ @port = 443
116
+ when false
117
+ # http
118
+ @port = 80
119
+ end
120
+
121
+ @access_key_id = options[:access_key_id]
122
+ @secret_access_key = options[:secret_access_key]
123
+
124
+ # Use proxy server if defined
125
+ # Based on patch by Mathias Dalheimer. 20070217
126
+ proxy = @proxy_server ? URI.parse(@proxy_server) : OpenStruct.new
127
+ @http = Net::HTTP::Proxy( proxy.host,
128
+ proxy.port,
129
+ proxy.user,
130
+ proxy.password).new(options[:server], @port)
131
+
132
+ @http.use_ssl = @use_ssl
133
+
134
+ # Don't verify the SSL certificates. Avoids SSL Cert warning in log on every GET.
135
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
136
+
137
+ end
138
+
139
+
140
+ private
141
+
142
+ # pathlist is a utility method which takes a key string and and array as input.
143
+ # It converts the array into a Hash with the hash key being 'Key.n' where
144
+ # 'n' increments by 1 for each iteration. So if you pass in args
145
+ # ("ImageId", ["123", "456"]) you should get
146
+ # {"ImageId.1"=>"123", "ImageId.2"=>"456"} returned.
147
+ def pathlist(key, arr)
148
+ params = {}
149
+ arr.each_with_index do |value, i|
150
+ params["#{key}.#{i+1}"] = value
151
+ end
152
+ params
153
+ end
154
+
155
+
156
+ # Make the connection to AWS EC2 passing in our request. This is generally called from
157
+ # within a 'Response' class object or one of its sub-classes so the response is interpreted
158
+ # in its proper context. See lib/EC2/responses.rb
159
+ def make_request(action, params, data='')
160
+
161
+ @http.start do
162
+
163
+ # remove any keys that have nil or empty values
164
+ params.reject! { |key, value| value.nil? or value.empty?}
165
+
166
+ params.merge!( {"Action" => action,
167
+ "SignatureVersion" => "2",
168
+ "SignatureMethod" => 'HmacSHA1',
169
+ "AWSAccessKeyId" => @access_key_id,
170
+ "Version" => API_VERSION,
171
+ "Timestamp"=>Time.now.getutc.iso8601} )
172
+
173
+ sig = get_aws_auth_param(params, @secret_access_key)
174
+
175
+ query = params.sort.collect do |param|
176
+ CGI::escape(param[0]) + "=" + CGI::escape(param[1])
177
+ end.join("&") + "&Signature=" + sig
178
+
179
+ req = Net::HTTP::Post.new("/")
180
+ req.content_type = 'application/x-www-form-urlencoded'
181
+ req['User-Agent'] = "github-amazon-ec2-ruby-gem"
182
+
183
+ response = @http.request(req, query)
184
+
185
+ # Make a call to see if we need to throw an error based on the response given by EC2
186
+ # All error classes are defined in EC2/exceptions.rb
187
+ ec2_error?(response)
188
+
189
+ return response
190
+
191
+ end
192
+
193
+ end
194
+
195
+ # Set the Authorization header using AWS signed header authentication
196
+ def get_aws_auth_param(params, secret_access_key)
197
+ canonical_string = EC2.canonical_string(params)
198
+ encoded_canonical = EC2.encode(secret_access_key, canonical_string)
199
+ end
200
+
201
+ # allow us to have a one line call in each method which will do all of the work
202
+ # in making the actual request to AWS.
203
+ def response_generator( options = {} )
204
+
205
+ options = {
206
+ :action => "",
207
+ :params => {}
208
+ }.merge(options)
209
+
210
+ raise ArgumentError, ":action must be provided to response_generator" if options[:action].nil? || options[:action].empty?
211
+
212
+ http_response = make_request(options[:action], options[:params])
213
+ http_xml = http_response.body
214
+ return Response.parse(:xml => http_xml)
215
+
216
+ end
217
+
218
+ # Raises the appropriate error if the specified Net::HTTPResponse object
219
+ # contains an Amazon EC2 error; returns +false+ otherwise.
220
+ def ec2_error?(response)
221
+
222
+ # return false if we got a HTTP 200 code,
223
+ # otherwise there is some type of error (40x,50x) and
224
+ # we should try to raise an appropriate exception
225
+ # from one of our exception classes defined in
226
+ # exceptions.rb
227
+ return false if response.is_a?(Net::HTTPSuccess)
228
+
229
+ # parse the XML document so we can walk through it
230
+ doc = REXML::Document.new(response.body)
231
+
232
+ # Check that the Error element is in the place we would expect.
233
+ # and if not raise a generic error exception
234
+ unless doc.root.elements['Errors'].elements['Error'].name == 'Error'
235
+ raise Error, "Unexpected error format. response.body is: #{response.body}"
236
+ end
237
+
238
+ # An valid error response looks like this:
239
+ # <?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>
240
+ # AWS EC2 throws some exception codes that look like Error.SubError. Since we can't name classes this way
241
+ # we need to strip out the '.' in the error 'Code' and we name the error exceptions with this
242
+ # non '.' name as well.
243
+ error_code = doc.root.elements['Errors'].elements['Error'].elements['Code'].text.gsub('.', '')
244
+ error_message = doc.root.elements['Errors'].elements['Error'].elements['Message'].text
245
+
246
+ # Raise one of our specific error classes if it exists.
247
+ # otherwise, throw a generic EC2 Error with a few details.
248
+ if EC2.const_defined?(error_code)
249
+ raise EC2.const_get(error_code), error_message
250
+ else
251
+ raise Error, "This is an undefined error code which needs to be added to exceptions.rb : error_code => #{error_code} : error_message => #{error_message}"
252
+ end
253
+
254
+ end
255
+
256
+ end
257
+
258
+ end
@@ -0,0 +1,41 @@
1
+ #--
2
+ # Amazon Web Services EC2 Query API Ruby library
3
+ #
4
+ # Ruby Gem Name:: amazon-ec2
5
+ # Author:: Glenn Rempe (mailto:glenn@rempe.us)
6
+ # Copyright:: Copyright (c) 2007-2008 Glenn Rempe
7
+ # License:: Distributes under the same terms as Ruby
8
+ # Home:: http://github.com/grempe/amazon-ec2/tree/master
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