krisr-amazon-ec2 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.gitignore +6 -0
  2. data/ChangeLog +284 -0
  3. data/LICENSE +66 -0
  4. data/README.rdoc +329 -0
  5. data/Rakefile +101 -0
  6. data/VERSION +1 -0
  7. data/amazon-ec2.gemspec +123 -0
  8. data/bin/ec2-gem-example.rb +66 -0
  9. data/bin/ec2-gem-profile.rb +10 -0
  10. data/bin/ec2sh +62 -0
  11. data/bin/setup.rb +26 -0
  12. data/deps.rip +1 -0
  13. data/lib/AWS.rb +239 -0
  14. data/lib/AWS/EC2.rb +67 -0
  15. data/lib/AWS/EC2/availability_zones.rb +43 -0
  16. data/lib/AWS/EC2/console.rb +46 -0
  17. data/lib/AWS/EC2/elastic_ips.rb +154 -0
  18. data/lib/AWS/EC2/image_attributes.rb +168 -0
  19. data/lib/AWS/EC2/images.rb +136 -0
  20. data/lib/AWS/EC2/instances.rb +218 -0
  21. data/lib/AWS/EC2/keypairs.rb +96 -0
  22. data/lib/AWS/EC2/products.rb +45 -0
  23. data/lib/AWS/EC2/security_groups.rb +234 -0
  24. data/lib/AWS/EC2/snapshots.rb +96 -0
  25. data/lib/AWS/EC2/volumes.rb +172 -0
  26. data/lib/AWS/ELB.rb +57 -0
  27. data/lib/AWS/ELB/load_balancers.rb +201 -0
  28. data/lib/AWS/exceptions.rb +169 -0
  29. data/lib/AWS/responses.rb +61 -0
  30. data/lib/EC2.rb +31 -0
  31. data/perftools/ec2prof +0 -0
  32. data/perftools/ec2prof-results.dot +193 -0
  33. data/perftools/ec2prof-results.txt +126 -0
  34. data/perftools/ec2prof.symbols +129 -0
  35. data/test/test_EC2.rb +68 -0
  36. data/test/test_EC2_availability_zones.rb +49 -0
  37. data/test/test_EC2_console.rb +54 -0
  38. data/test/test_EC2_elastic_ips.rb +144 -0
  39. data/test/test_EC2_image_attributes.rb +238 -0
  40. data/test/test_EC2_images.rb +197 -0
  41. data/test/test_EC2_instances.rb +348 -0
  42. data/test/test_EC2_keypairs.rb +123 -0
  43. data/test/test_EC2_products.rb +48 -0
  44. data/test/test_EC2_responses.rb +52 -0
  45. data/test/test_EC2_s3_xmlsimple.rb +80 -0
  46. data/test/test_EC2_security_groups.rb +205 -0
  47. data/test/test_EC2_snapshots.rb +83 -0
  48. data/test/test_EC2_volumes.rb +142 -0
  49. data/test/test_ELB_load_balancers.rb +238 -0
  50. data/test/test_helper.rb +19 -0
  51. data/wsdl/2007-08-29.ec2.wsdl +1269 -0
  52. data/wsdl/2008-02-01.ec2.wsdl +1614 -0
  53. data/wsdl/2008-05-05.ec2.wsdl +2052 -0
  54. data/wsdl/2008-12-01.ec2.wsdl +2354 -0
  55. metadata +184 -0
@@ -0,0 +1,101 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "amazon-ec2"
8
+ gem.summary = %Q{Amazon EC2 Ruby Gem}
9
+ gem.description = %Q{An interface library that allows Ruby applications to easily connect to the HTTP 'Query API' for the Amazon Web Services Elastic Compute Cloud (EC2) and manipulate cloud servers.}
10
+ gem.email = "glenn@rempe.us"
11
+ gem.homepage = "http://github.com/grempe/amazon-ec2"
12
+ gem.authors = ["Glenn Rempe"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+
15
+ # gem.autorequire = 'EC2'
16
+ gem.rdoc_options = ["--quiet", "--title", "amazon-ec2 documentation", "--opname", "index.html", "--line-numbers", "--main", "README.rdoc", "--inline-source"]
17
+
18
+ gem.rubyforge_project = 'amazon-ec2'
19
+
20
+ gem.add_dependency('xml-simple', '>= 1.0.12')
21
+ gem.add_development_dependency('mocha', '>= 0.9.5')
22
+ gem.add_development_dependency('test-spec', '>= 0.10.0')
23
+ gem.add_development_dependency('rcov', '>= 0.8.1.2.0')
24
+ gem.add_development_dependency('perftools.rb', '= 0.1.6')
25
+
26
+ end
27
+
28
+ rescue LoadError
29
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
30
+ end
31
+
32
+ require 'rake/testtask'
33
+ Rake::TestTask.new(:test) do |test|
34
+ test.libs << 'lib' << 'test'
35
+ test.pattern = 'test/**/test_*.rb'
36
+ test.verbose = true
37
+ end
38
+
39
+ begin
40
+ require 'rcov/rcovtask'
41
+ Rcov::RcovTask.new do |test|
42
+ test.libs << 'test'
43
+ test.pattern = 'test/**/test_*.rb'
44
+ test.verbose = true
45
+ end
46
+ rescue LoadError
47
+ task :rcov do
48
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
49
+ end
50
+ end
51
+
52
+
53
+ task :default => :test
54
+
55
+ require 'rake/rdoctask'
56
+ Rake::RDocTask.new do |rdoc|
57
+ if File.exist?('VERSION.yml')
58
+ config = YAML.load(File.read('VERSION.yml'))
59
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
60
+ else
61
+ version = ""
62
+ end
63
+
64
+ rdoc.rdoc_dir = 'rdoc'
65
+ rdoc.title = "amazon-ec2 #{version}"
66
+ rdoc.rdoc_files.include('README*')
67
+ rdoc.rdoc_files.include('lib/**/*.rb')
68
+ end
69
+
70
+ begin
71
+ require 'rake/contrib/sshpublisher'
72
+ namespace :rubyforge do
73
+
74
+ desc "Release gem and RDoc documentation to RubyForge"
75
+ task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
76
+
77
+ namespace :release do
78
+ desc "Publish RDoc to RubyForge."
79
+ task :docs => [:rdoc] do
80
+ config = YAML.load(
81
+ File.read(File.expand_path('~/.rubyforge/user-config.yml'))
82
+ )
83
+
84
+ host = "#{config['username']}@rubyforge.org"
85
+ remote_dir = "/var/www/gforge-projects/amazon-ec2/"
86
+ local_dir = 'rdoc'
87
+
88
+ Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
89
+ end
90
+ end
91
+ end
92
+ rescue LoadError
93
+ puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
94
+ end
95
+
96
+ desc "Generate a perftools.rb profile"
97
+ task :profile do
98
+ system("CPUPROFILE=perftools/ec2prof RUBYOPT='-r/usr/local/lib/ruby/gems/1.8/gems/perftools.rb-0.1.6/lib/perftools.bundle' ruby -r'rubygems' bin/ec2-gem-profile.rb")
99
+ system("pprof.rb --text --ignore=Gem perftools/ec2prof > perftools/ec2prof-results.txt")
100
+ system("pprof.rb --dot --ignore=Gem perftools/ec2prof > perftools/ec2prof-results.dot")
101
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.0
@@ -0,0 +1,123 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{amazon-ec2}
5
+ s.version = "0.5.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Glenn Rempe"]
9
+ s.date = %q{2009-08-04}
10
+ s.description = %q{An interface library that allows Ruby applications to easily connect to the HTTP 'Query API' for the Amazon Web Services Elastic Compute Cloud (EC2) and manipulate cloud servers.}
11
+ s.email = %q{glenn@rempe.us}
12
+ s.executables = ["ec2-gem-example.rb", "ec2-gem-profile.rb", "ec2sh", "setup.rb"]
13
+ s.extra_rdoc_files = [
14
+ "ChangeLog",
15
+ "LICENSE",
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "ChangeLog",
21
+ "LICENSE",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "amazon-ec2.gemspec",
26
+ "bin/ec2-gem-example.rb",
27
+ "bin/ec2-gem-profile.rb",
28
+ "bin/ec2sh",
29
+ "bin/setup.rb",
30
+ "deps.rip",
31
+ "lib/AWS.rb",
32
+ "lib/AWS/EC2.rb",
33
+ "lib/AWS/EC2/availability_zones.rb",
34
+ "lib/AWS/EC2/console.rb",
35
+ "lib/AWS/EC2/elastic_ips.rb",
36
+ "lib/AWS/EC2/image_attributes.rb",
37
+ "lib/AWS/EC2/images.rb",
38
+ "lib/AWS/EC2/instances.rb",
39
+ "lib/AWS/EC2/keypairs.rb",
40
+ "lib/AWS/EC2/products.rb",
41
+ "lib/AWS/EC2/security_groups.rb",
42
+ "lib/AWS/EC2/snapshots.rb",
43
+ "lib/AWS/EC2/volumes.rb",
44
+ "lib/AWS/ELB.rb",
45
+ "lib/AWS/ELB/load_balancers.rb",
46
+ "lib/AWS/exceptions.rb",
47
+ "lib/AWS/responses.rb",
48
+ "lib/EC2.rb",
49
+ "perftools/ec2prof",
50
+ "perftools/ec2prof-results.dot",
51
+ "perftools/ec2prof-results.txt",
52
+ "perftools/ec2prof.symbols",
53
+ "test/test_EC2.rb",
54
+ "test/test_EC2_availability_zones.rb",
55
+ "test/test_EC2_console.rb",
56
+ "test/test_EC2_elastic_ips.rb",
57
+ "test/test_EC2_image_attributes.rb",
58
+ "test/test_EC2_images.rb",
59
+ "test/test_EC2_instances.rb",
60
+ "test/test_EC2_keypairs.rb",
61
+ "test/test_EC2_products.rb",
62
+ "test/test_EC2_responses.rb",
63
+ "test/test_EC2_s3_xmlsimple.rb",
64
+ "test/test_EC2_security_groups.rb",
65
+ "test/test_EC2_snapshots.rb",
66
+ "test/test_EC2_volumes.rb",
67
+ "test/test_ELB_load_balancers.rb",
68
+ "test/test_helper.rb",
69
+ "wsdl/2007-08-29.ec2.wsdl",
70
+ "wsdl/2008-02-01.ec2.wsdl",
71
+ "wsdl/2008-05-05.ec2.wsdl",
72
+ "wsdl/2008-12-01.ec2.wsdl"
73
+ ]
74
+ s.homepage = %q{http://github.com/grempe/amazon-ec2}
75
+ s.rdoc_options = ["--quiet", "--title", "amazon-ec2 documentation", "--opname", "index.html", "--line-numbers", "--main", "README.rdoc", "--inline-source"]
76
+ s.require_paths = ["lib"]
77
+ s.rubyforge_project = %q{amazon-ec2}
78
+ s.rubygems_version = %q{1.3.4}
79
+ s.summary = %q{Amazon EC2 Ruby Gem}
80
+ s.test_files = [
81
+ "test/test_EC2.rb",
82
+ "test/test_EC2_availability_zones.rb",
83
+ "test/test_EC2_console.rb",
84
+ "test/test_EC2_elastic_ips.rb",
85
+ "test/test_EC2_image_attributes.rb",
86
+ "test/test_EC2_images.rb",
87
+ "test/test_EC2_instances.rb",
88
+ "test/test_EC2_keypairs.rb",
89
+ "test/test_EC2_products.rb",
90
+ "test/test_EC2_responses.rb",
91
+ "test/test_EC2_s3_xmlsimple.rb",
92
+ "test/test_EC2_security_groups.rb",
93
+ "test/test_EC2_snapshots.rb",
94
+ "test/test_EC2_volumes.rb",
95
+ "test/test_ELB_load_balancers.rb",
96
+ "test/test_helper.rb"
97
+ ]
98
+
99
+ if s.respond_to? :specification_version then
100
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
101
+ s.specification_version = 3
102
+
103
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
104
+ s.add_runtime_dependency(%q<xml-simple>, [">= 1.0.12"])
105
+ s.add_development_dependency(%q<mocha>, [">= 0.9.5"])
106
+ s.add_development_dependency(%q<test-spec>, [">= 0.10.0"])
107
+ s.add_development_dependency(%q<rcov>, [">= 0.8.1.2.0"])
108
+ s.add_development_dependency(%q<perftools.rb>, ["= 0.1.6"])
109
+ else
110
+ s.add_dependency(%q<xml-simple>, [">= 1.0.12"])
111
+ s.add_dependency(%q<mocha>, [">= 0.9.5"])
112
+ s.add_dependency(%q<test-spec>, [">= 0.10.0"])
113
+ s.add_dependency(%q<rcov>, [">= 0.8.1.2.0"])
114
+ s.add_dependency(%q<perftools.rb>, ["= 0.1.6"])
115
+ end
116
+ else
117
+ s.add_dependency(%q<xml-simple>, [">= 1.0.12"])
118
+ s.add_dependency(%q<mocha>, [">= 0.9.5"])
119
+ s.add_dependency(%q<test-spec>, [">= 0.10.0"])
120
+ s.add_dependency(%q<rcov>, [">= 0.8.1.2.0"])
121
+ s.add_dependency(%q<perftools.rb>, ["= 0.1.6"])
122
+ end
123
+ end
@@ -0,0 +1,66 @@
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 File.dirname(__FILE__) + '/../lib/AWS'
13
+ require 'pp'
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
+ # us-east-1.ec2.amazonaws.com == ec2.amazonaws.com
33
+ # eu-west-1.ec2.amazonaws.com for the european region
34
+ # test different servers by running something like:
35
+ # export EC2_URL='https://ec2.amazonaws.com';./bin/ec2-gem-example.rb
36
+ if ENV['EC2_URL']
37
+ ec2 = AWS::EC2::Base.new( :access_key_id => ACCESS_KEY_ID, :secret_access_key => SECRET_ACCESS_KEY, :server => URI.parse(ENV['EC2_URL']).host )
38
+ else
39
+ # default server is US ec2.amazonaws.com
40
+ ec2 = AWS::EC2::Base.new( :access_key_id => ACCESS_KEY_ID, :secret_access_key => SECRET_ACCESS_KEY )
41
+ end
42
+
43
+ puts "----- ec2.methods.sort -----"
44
+ p ec2.methods.sort
45
+
46
+ puts "----- listing images owned by 'amazon' -----"
47
+ ec2.describe_images(:owner_id => "amazon").imagesSet.item.each do |image|
48
+ image.keys.each do |key|
49
+ puts "#{key} => #{image[key]}"
50
+ end
51
+ end
52
+
53
+ puts "----- listing all running instances -----"
54
+ pp ec2.describe_instances()
55
+
56
+ puts "----- creating a security group -----"
57
+ pp ec2.create_security_group(:group_name => "ec2-example-rb-test-group", :group_description => "ec-example.rb test group description.")
58
+
59
+ puts "----- listing security groups -----"
60
+ pp ec2.describe_security_groups()
61
+
62
+ puts "----- deleting a security group -----"
63
+ pp ec2.delete_security_group(:group_name => "ec2-example-rb-test-group")
64
+
65
+ puts "----- listing my keypairs (verbose mode) -----"
66
+ pp ec2.describe_keypairs()
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Basic single command application that we can call with perftools.rb to get consistent results.
4
+
5
+ require File.dirname(__FILE__) + '/../lib/AWS'
6
+ ACCESS_KEY_ID = ENV['AMAZON_ACCESS_KEY_ID']
7
+ SECRET_ACCESS_KEY = ENV['AMAZON_SECRET_ACCESS_KEY']
8
+ ec2 = AWS::EC2::Base.new( :access_key_id => ACCESS_KEY_ID, :secret_access_key => SECRET_ACCESS_KEY )
9
+ @images = ec2.describe_images
10
+
@@ -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
+ # => [#<AWS::EC2::Item image_location...
23
+
24
+ aws_lib = File.dirname(__FILE__) + '/../lib/AWS'
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 AWS::Response objects, each an EC2 image and its data
52
+ >> @ec2.describe_images.imagesSet.item
53
+ >> @ec2.describe_images.imagesSet.item[0] (a hash representing a single item in that array)
54
+ >> @ec2.describe_images.imagesSet.item[0].to_s (a String representation of that item)
55
+
56
+ MESSAGE
57
+
58
+ puts welcome_message
59
+ exec "#{irb_name} -r #{aws_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
@@ -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 = AWS::EC2::Base.new(opts)
21
+ @elb = AWS::ELB::Base.new(opts)
22
+ end
23
+
24
+ puts "EC2 Server: #{opts[:server]}"
25
+
26
+ include AWS
@@ -0,0 +1 @@
1
+ xml-simple 1.0.12
@@ -0,0 +1,239 @@
1
+ # Require any lib files that we have bundled with this Ruby Gem in the lib/AWS directory.
2
+ # Parts of the AWS module and Base class are broken out into separate
3
+ # files for maintainability and are organized by the functional groupings defined
4
+ # in the AWS API developers guide.
5
+
6
+
7
+ %w[ base64 cgi openssl digest/sha1 net/https rexml/document time ostruct ].each { |f| require f }
8
+
9
+ module AWS
10
+ # Builds the canonical string for signing. This strips out all '&', '?', and '='
11
+ # from the query string to be signed.
12
+ # Note: The parameters in the path passed in must already be sorted in
13
+ # case-insensitive alphabetical order and must not be url encoded.
14
+ def AWS.canonical_string(params, host, method="POST", base="/")
15
+ # Sort, and encode parameters into a canonical string.
16
+ sorted_params = params.sort {|x,y| x[0] <=> y[0]}
17
+ encoded_params = sorted_params.collect do |p|
18
+ encoded = (CGI::escape(p[0].to_s) +
19
+ "=" + CGI::escape(p[1].to_s))
20
+ # Ensure spaces are encoded as '%20', not '+'
21
+ encoded.gsub('+', '%20')
22
+ end
23
+ sigquery = encoded_params.join("&")
24
+
25
+ # Generate the request description string
26
+ req_desc =
27
+ method + "\n" +
28
+ host + "\n" +
29
+ base + "\n" +
30
+ sigquery
31
+
32
+ end
33
+
34
+ # Encodes the given string with the secret_access_key, by taking the
35
+ # hmac-sha1 sum, and then base64 encoding it. Optionally, it will also
36
+ # url encode the result of that to protect the string if it's going to
37
+ # be used as a query string parameter.
38
+ def AWS.encode(secret_access_key, str, urlencode=true)
39
+ digest = OpenSSL::Digest::Digest.new('sha1')
40
+ b64_hmac =
41
+ Base64.encode64(
42
+ OpenSSL::HMAC.digest(digest, secret_access_key, str)).gsub("\n","")
43
+
44
+ if urlencode
45
+ return CGI::escape(b64_hmac)
46
+ else
47
+ return b64_hmac
48
+ end
49
+ end
50
+
51
+ class Base
52
+
53
+ attr_reader :use_ssl, :server, :proxy_server, :port
54
+
55
+ def initialize( options = {} )
56
+
57
+ options = { :access_key_id => "",
58
+ :secret_access_key => "",
59
+ :use_ssl => true,
60
+ :server => default_host,
61
+ :proxy_server => nil
62
+ }.merge(options)
63
+
64
+ @server = options[:server]
65
+ @proxy_server = options[:proxy_server]
66
+ @use_ssl = options[:use_ssl]
67
+
68
+ raise ArgumentError, "No :access_key_id provided" if options[:access_key_id].nil? || options[:access_key_id].empty?
69
+ raise ArgumentError, "No :secret_access_key provided" if options[:secret_access_key].nil? || options[:secret_access_key].empty?
70
+ raise ArgumentError, "No :use_ssl value provided" if options[:use_ssl].nil?
71
+ raise ArgumentError, "Invalid :use_ssl value provided, only 'true' or 'false' allowed" unless options[:use_ssl] == true || options[:use_ssl] == false
72
+ raise ArgumentError, "No :server provided" if options[:server].nil? || options[:server].empty?
73
+
74
+ if options[:port]
75
+ # user-specified port
76
+ @port = options[:port]
77
+ elsif @use_ssl
78
+ # https
79
+ @port = 443
80
+ else
81
+ # http
82
+ @port = 80
83
+ end
84
+
85
+ @access_key_id = options[:access_key_id]
86
+ @secret_access_key = options[:secret_access_key]
87
+
88
+ # Use proxy server if defined
89
+ # Based on patch by Mathias Dalheimer. 20070217
90
+ proxy = @proxy_server ? URI.parse(@proxy_server) : OpenStruct.new
91
+ @http = Net::HTTP::Proxy( proxy.host,
92
+ proxy.port,
93
+ proxy.user,
94
+ proxy.password).new(options[:server], @port)
95
+
96
+ @http.use_ssl = @use_ssl
97
+
98
+ # Don't verify the SSL certificates. Avoids SSL Cert warning in log on every GET.
99
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
100
+
101
+ end
102
+
103
+
104
+ protected
105
+
106
+ # pathlist is a utility method which takes a key string and and array as input.
107
+ # It converts the array into a Hash with the hash key being 'Key.n' where
108
+ # 'n' increments by 1 for each iteration. So if you pass in args
109
+ # ("ImageId", ["123", "456"]) you should get
110
+ # {"ImageId.1"=>"123", "ImageId.2"=>"456"} returned.
111
+ def pathlist(key, arr)
112
+ params = {}
113
+ arr.each_with_index do |value, i|
114
+ params["#{key}.#{i+1}"] = value
115
+ end
116
+ params
117
+ end
118
+
119
+ # Same as _pathlist_ except it deals with arrays of hashes.
120
+ # So if you pass in args
121
+ # ("People", [{:name=>'jon', :age=>'22'}, {:name=>'chris'}], {:name => 'Name', :age => 'Age'}) you should get
122
+ # {"People.1.Name"=>"jon", "People.1.Age"=>'22', 'People.2.Name'=>'chris'}
123
+ def pathhashlist(key, arr_of_hashes, mappings)
124
+ params ={}
125
+ arr_of_hashes.each_with_index do |hash, i|
126
+ hash.each do |attribute, value|
127
+ params["#{key}.#{i+1}.#{mappings[attribute]}"] = value
128
+ end
129
+ end
130
+ params
131
+ end
132
+
133
+ # Make the connection to AWS EC2 passing in our request. This is generally called from
134
+ # within a 'Response' class object or one of its sub-classes so the response is interpreted
135
+ # in its proper context. See lib/EC2/responses.rb
136
+ def make_request(action, params, data='')
137
+
138
+ @http.start do
139
+
140
+ # remove any keys that have nil or empty values
141
+ params.reject! { |key, value| value.nil? or value.empty?}
142
+
143
+ params.merge!( {"Action" => action,
144
+ "SignatureVersion" => "2",
145
+ "SignatureMethod" => 'HmacSHA1',
146
+ "AWSAccessKeyId" => @access_key_id,
147
+ "Version" => api_version,
148
+ "Timestamp"=>Time.now.getutc.iso8601} )
149
+
150
+ sig = get_aws_auth_param(params, @secret_access_key, @server)
151
+
152
+ query = params.sort.collect do |param|
153
+ CGI::escape(param[0]) + "=" + CGI::escape(param[1])
154
+ end.join("&") + "&Signature=" + sig
155
+
156
+ req = Net::HTTP::Post.new("/")
157
+ req.content_type = 'application/x-www-form-urlencoded'
158
+ req['User-Agent'] = "github-amazon-ec2-ruby-gem"
159
+ response = @http.request(req, query)
160
+
161
+ # Make a call to see if we need to throw an error based on the response given by EC2
162
+ # All error classes are defined in EC2/exceptions.rb
163
+ aws_error?(response)
164
+
165
+ return response
166
+
167
+ end
168
+
169
+ end
170
+
171
+ # Set the Authorization header using AWS signed header authentication
172
+ def get_aws_auth_param(params, secret_access_key, server)
173
+ canonical_string = AWS.canonical_string(params, server)
174
+ encoded_canonical = AWS.encode(secret_access_key, canonical_string)
175
+ end
176
+
177
+ # allow us to have a one line call in each method which will do all of the work
178
+ # in making the actual request to AWS.
179
+ def response_generator( options = {} )
180
+
181
+ options = {
182
+ :action => "",
183
+ :params => {}
184
+ }.merge(options)
185
+
186
+ raise ArgumentError, ":action must be provided to response_generator" if options[:action].nil? || options[:action].empty?
187
+
188
+ http_response = make_request(options[:action], options[:params])
189
+ http_xml = http_response.body
190
+ return Response.parse(:xml => http_xml)
191
+
192
+ end
193
+
194
+ # Raises the appropriate error if the specified Net::HTTPResponse object
195
+ # contains an Amazon EC2 error; returns +false+ otherwise.
196
+ def aws_error?(response)
197
+
198
+ # return false if we got a HTTP 200 code,
199
+ # otherwise there is some type of error (40x,50x) and
200
+ # we should try to raise an appropriate exception
201
+ # from one of our exception classes defined in
202
+ # exceptions.rb
203
+ return false if response.is_a?(Net::HTTPSuccess)
204
+
205
+ # parse the XML document so we can walk through it
206
+ doc = REXML::Document.new(response.body)
207
+
208
+ # Check that the Error element is in the place we would expect.
209
+ # and if not raise a generic error exception
210
+ if doc.root.elements['Error']
211
+ error_element = doc.root.elements['Error']
212
+ elsif doc.root.elements['Errors'] && doc.root.elements['Errors'].elements['Error'] && doc.root.elements['Errors'].elements['Error'].name == 'Error'
213
+ error_element = doc.root.elements['Errors'].elements['Error']
214
+ else
215
+ raise Error, "Unexpected error format. response.body is: #{response.body}"
216
+ end
217
+
218
+ # An valid error response looks like this:
219
+ # <?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>
220
+ # AWS EC2 throws some exception codes that look like Error.SubError. Since we can't name classes this way
221
+ # we need to strip out the '.' in the error 'Code' and we name the error exceptions with this
222
+ # non '.' name as well.
223
+ error_code = error_element.elements['Code'].text.gsub('.', '')
224
+ error_message = error_element.elements['Message'].text
225
+
226
+ # Raise one of our specific error classes if it exists.
227
+ # otherwise, throw a generic EC2 Error with a few details.
228
+ if AWS.const_defined?(error_code)
229
+ raise AWS.const_get(error_code), error_message
230
+ else
231
+ raise AWS::Error, error_message
232
+ end
233
+
234
+ end
235
+
236
+ end
237
+ end
238
+
239
+ Dir[File.join(File.dirname(__FILE__), 'AWS/*.rb')].sort.each { |lib| require lib }