kunley-amazon-ec2 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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