From ce557e2d3f44b69b367baf474ce0ad874b1fd372 Mon Sep 17 00:00:00 2001 From: Junya Ogura Date: Sun, 8 May 2011 05:00:21 +0900 Subject: [PATCH 01/24] =?UTF-8?q?=E4=BE=9D=E5=AD=98gem=E3=82=92=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E3=81=97=E3=81=9F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile.lock | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c55d84b..5d5560f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,15 +7,13 @@ PATH GEM remote: http://rubygems.org/ specs: - mocha (0.9.9) - rake - perftools.rb (0.5.4) - rake (0.8.7) + mocha (0.9.12) + perftools.rb (0.5.6) rcov (0.9.9) test-spec (0.10.0) - test-unit (2.1.2) - xml-simple (1.0.12) - yard (0.6.2) + test-unit (2.3.0) + xml-simple (1.0.15) + yard (0.6.8) PLATFORMS ruby @@ -27,5 +25,4 @@ DEPENDENCIES rcov (>= 0.9.9) test-spec (>= 0.10.0) test-unit (>= 2.1.2) - xml-simple (>= 1.0.12) yard (>= 0.6.2) From 581dbdbba203072ba50016101c303247d3d2d819 Mon Sep 17 00:00:00 2001 From: Junya Ogura Date: Sun, 8 May 2011 05:07:17 +0900 Subject: [PATCH 02/24] added error handling for Cloudwatch. --- lib/AWS.rb | 14 +++++++++++--- test/test_Cloudwatch.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 test/test_Cloudwatch.rb diff --git a/lib/AWS.rb b/lib/AWS.rb index 23afd2c..21c77d9 100644 --- a/lib/AWS.rb +++ b/lib/AWS.rb @@ -346,17 +346,25 @@ def aws_error?(response) # Check that the Error element is in the place we would expect. # and if not raise a generic error exception - unless doc.root.elements['Errors'].elements['Error'].name == 'Error' + if doc.root.elements['Error'] and doc.root.elements['Error'].name == 'Error' + error_elem = doc.root.elements['Error'] + elsif doc.root.elements['Errors'].elements['Error'].name == 'Error' + error_elem = doc.root.elements['Errors'].elements['Error'] + else raise Error, "Unexpected error format. response.body is: #{response.body}" end # An valid error response looks like this: # InvalidParameterCombinationUnknown parameter: foo291cef62-3e86-414b-900e-17246eccfae8 + # + # or this: + # SenderInvalidClientTokenIdThe security token included in the request is invalid1e77e1bb-2920-11e0-80c8-b71648ee0b72 + # # AWS throws some exception codes that look like Error.SubError. Since we can't name classes this way # we need to strip out the '.' in the error 'Code' and we name the error exceptions with this # non '.' name as well. - error_code = doc.root.elements['Errors'].elements['Error'].elements['Code'].text.gsub('.', '') - error_message = doc.root.elements['Errors'].elements['Error'].elements['Message'].text + error_code = error_elem.elements['Code'].text.gsub('.', '') + error_message = error_elem.elements['Message'].text # Raise one of our specific error classes if it exists. # otherwise, throw a generic EC2 Error with a few details. diff --git a/test/test_Cloudwatch.rb b/test/test_Cloudwatch.rb new file mode 100644 index 0000000..eaa4174 --- /dev/null +++ b/test/test_Cloudwatch.rb @@ -0,0 +1,26 @@ +require File.dirname(__FILE__) + '/test_helper.rb' + +context "cloudwatch " do + before do + @cw = AWS::Cloudwatch::Base.new( :access_key_id => "not a key", :secret_access_key => "not a secret" ) + + @error_response_for_invalid_security_token = <<-RESPONSE + + + Sender + InvalidClientTokenId + The security token included in the request is invalid + + 1e77e1bb-2920-11e0-80c8-b71648ee0b72 + + RESPONSE + end + + specify "AWS::Cloudwatch::Base should give back a nice response if there is an error" do + @cw.stubs(:make_request).with('ListMetrics', {}).returns stub(:body => @error_response_for_invalid_security_token, :is_a? => true) + + response = @cw.list_metrics + response.should.be.an.instance_of Hash + response["Error"]["Message"].should.equal "The security token included in the request is invalid" + end +end From 0c605523cca707374c6d46ab5838a1cc6ed01a9c Mon Sep 17 00:00:00 2001 From: Junya Ogura Date: Sun, 8 May 2011 05:09:08 +0900 Subject: [PATCH 03/24] add NextToken support to Cloudwatch ListMetrics API. --- lib/AWS/Cloudwatch/monitoring.rb | 11 ++++++++--- test/test_Cloudwatch.rb | 5 +++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/AWS/Cloudwatch/monitoring.rb b/lib/AWS/Cloudwatch/monitoring.rb index 538debe..28778c9 100644 --- a/lib/AWS/Cloudwatch/monitoring.rb +++ b/lib/AWS/Cloudwatch/monitoring.rb @@ -6,9 +6,14 @@ class Base < AWS::Base # account. To get further information from the metrics, you'll then need to # call get_metric_statistics. # - # there are no options available to this method. - def list_metrics - return response_generator(:action => 'ListMetrics', :params => {}) + # @option options [String] :next_token Next token + def list_metrics( options ={} ) + options = { :next_token => nil }.merge(options) + + params = {} + params['NextToken'] = options[:next_token] if options[:next_token] + + return response_generator(:action => 'ListMetrics', :params => params) end # get_metric_statistics pulls a hashed array from Cloudwatch with the stats diff --git a/test/test_Cloudwatch.rb b/test/test_Cloudwatch.rb index eaa4174..9726510 100644 --- a/test/test_Cloudwatch.rb +++ b/test/test_Cloudwatch.rb @@ -23,4 +23,9 @@ response.should.be.an.instance_of Hash response["Error"]["Message"].should.equal "The security token included in the request is invalid" end + + specify "AWS::Cloudwatch::Base should take a next_token parameter" do + @cw.expects(:make_request).with('ListMetrics', {'NextToken' => 'aaaaaaaaaa'}).returns stub(:body => @error_response_for_invalid_security_token) + @cw.list_metrics(:next_token => 'aaaaaaaaaa') + end end From 605cd38d9f4d64e07d3dba7d5567d329b6d6493d Mon Sep 17 00:00:00 2001 From: Junya Ogura Date: Sun, 8 May 2011 05:10:30 +0900 Subject: [PATCH 04/24] update ChangeLog. --- ChangeLog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ChangeLog b/ChangeLog index 2b884da..ece5986 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +=== x.x.x + * add NextToken support to Cloudwatch ListMetrics API. (juno) + * added error handling for Cloudwatch. (juno) === 0.9.17 2010-11-21 * Converted from Jeweler to Bundler, 'bundle install' to install dependencies From 2d7196437dd038cfc43e64fd3c872471c1c49339 Mon Sep 17 00:00:00 2001 From: Junya Ogura Date: Sun, 8 May 2011 05:12:34 +0900 Subject: [PATCH 05/24] added Filters for instances and volumes. --- ChangeLog | 1 + lib/AWS/EC2/instances.rb | 5 ++++- lib/AWS/EC2/volumes.rb | 5 ++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index ece5986..307f801 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ === x.x.x * add NextToken support to Cloudwatch ListMetrics API. (juno) * added error handling for Cloudwatch. (juno) + * added Filters for instances and volumes. === 0.9.17 2010-11-21 * Converted from Jeweler to Bundler, 'bundle install' to install dependencies diff --git a/lib/AWS/EC2/instances.rb b/lib/AWS/EC2/instances.rb index 71f7653..cc73164 100644 --- a/lib/AWS/EC2/instances.rb +++ b/lib/AWS/EC2/instances.rb @@ -85,11 +85,14 @@ def run_instances( options = {} ) # Recently terminated instances will be included in the returned results for a small interval subsequent to # their termination. This interval is typically of the order of one hour # - # @option options [Array] :instance_id ([]) + # @option options [Array] :filter ([]) # def describe_instances( options = {} ) options = { :instance_id => [] }.merge(options) params = pathlist("InstanceId", options[:instance_id]) + if options[:filter] + params.merge!(pathkvlist('Filter', options[:filter], 'Name', 'Value', {})) + end return response_generator(:action => "DescribeInstances", :params => params) end diff --git a/lib/AWS/EC2/volumes.rb b/lib/AWS/EC2/volumes.rb index dfdbece..60ef6e6 100644 --- a/lib/AWS/EC2/volumes.rb +++ b/lib/AWS/EC2/volumes.rb @@ -5,11 +5,14 @@ class Base < AWS::Base # The DescribeVolumes operation lists one or more Amazon EBS volumes that you own, If you do not specify any volumes, Amazon EBS returns all volumes that you own. # - # @option options [optional, String] :volume_id ([]) + # @option options [Array] :filter ([]) # def describe_volumes( options = {} ) options = { :volume_id => [] }.merge(options) params = pathlist("VolumeId", options[:volume_id] ) + if options[:filter] + params.merge!(pathkvlist('Filter', options[:filter], 'Name', 'Value', {})) + end return response_generator(:action => "DescribeVolumes", :params => params) end From b0d64957bf47528375202eebe45ed0216b43830a Mon Sep 17 00:00:00 2001 From: Junya Ogura Date: Sun, 8 May 2011 05:17:21 +0900 Subject: [PATCH 06/24] add ImportKeyPair API. --- ChangeLog | 3 ++- lib/AWS/EC2/keypairs.rb | 17 +++++++++++++++++ test/test_EC2_keypairs.rb | 22 ++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 307f801..91f20ac 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,8 @@ === x.x.x * add NextToken support to Cloudwatch ListMetrics API. (juno) * added error handling for Cloudwatch. (juno) - * added Filters for instances and volumes. + * added Filters for instances and volumes. (skade) + * add EC2 ImportKeyPair API. (juno) === 0.9.17 2010-11-21 * Converted from Jeweler to Bundler, 'bundle install' to install dependencies diff --git a/lib/AWS/EC2/keypairs.rb b/lib/AWS/EC2/keypairs.rb index ff58f17..4e67d9c 100644 --- a/lib/AWS/EC2/keypairs.rb +++ b/lib/AWS/EC2/keypairs.rb @@ -41,6 +41,23 @@ def delete_keypair( options = {} ) end + # The ImportKeyPair operation upload a new RSA public key to the Amazon AWS. + # + # @option options [String] :key_name + # @option options [String] :public_key_material + # + def import_keypair( options = {} ) + options = { :key_name => "", :public_key_material => "" }.merge(options) + raise ArgumentError, "No :key_name provided" if options[:key_name].nil? || options[:key_name].empty? + raise ArgumentError, "No :public_key_material provided" if options[:public_key_material].nil? || options[:public_key_material].empty? + params = { + "KeyName" => options[:key_name], + "PublicKeyMaterial" => Base64.encode64(options[:public_key_material]).gsub(/\n/, "").strip + } + return response_generator(:action => "ImportKeyPair", :params => params) + end + + end end end diff --git a/test/test_EC2_keypairs.rb b/test/test_EC2_keypairs.rb index f56254b..fe6d2dc 100644 --- a/test/test_EC2_keypairs.rb +++ b/test/test_EC2_keypairs.rb @@ -62,6 +62,16 @@ RESPONSE + @import_keypair_body = <<-RESPONSE + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + example-key-name + + 1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f + + + RESPONSE + end @@ -120,4 +130,16 @@ end + specify "should be able to be import public key with import_keypair" do + public_key_material = 'ssh-rsa AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' + encoded_public_key_material = 'c3NoLXJzYSBBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBPQ==' + @ec2.stubs(:make_request).with('ImportKeyPair', {"KeyName"=>"example-key-name", "PublicKeyMaterial" => encoded_public_key_material}). + returns stub(:body => @import_keypair_body, :is_a? => true) + @ec2.import_keypair( :key_name => "example-key-name", :public_key_material => public_key_material ).should.be.an.instance_of Hash + response = @ec2.import_keypair( :key_name => "example-key-name", :public_key_material => public_key_material ) + response.keyName.should.equal "example-key-name" + response.keyFingerprint.strip.should.equal "1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f" + end + + end From 3b932e9bf7f548702825d75f357cd92b1861d77b Mon Sep 17 00:00:00 2001 From: Junya Ogura Date: Sun, 8 May 2011 06:24:05 +0900 Subject: [PATCH 07/24] update EC2 API Version to 2011-02-28. - EC2#authorize_security_group_ingress : support API Version 2011-02-28 - EC2#revoke_security_group_ingress : support API Version 2011-02-28 --- ChangeLog | 4 ++ Gemfile.lock | 2 + amazon-ec2.gemspec | 1 + lib/AWS/EC2.rb | 2 +- lib/AWS/EC2/security_groups.rb | 110 ++++++++++++++++++++----------- test/test_EC2_security_groups.rb | 44 ++++++++----- 6 files changed, 104 insertions(+), 59 deletions(-) diff --git a/ChangeLog b/ChangeLog index 91f20ac..322d423 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,10 @@ * added error handling for Cloudwatch. (juno) * added Filters for instances and volumes. (skade) * add EC2 ImportKeyPair API. (juno) + * EC2#describe_security_groups : add :group_id option (juno) + * Update EC2 API Version to 2011-02-28 (juno) + * EC2#authorize_security_group_ingress : support API Version 2011-02-28 (juno) + * EC2#revoke_security_group_ingress : support API Version 2011-02-28 (juno) === 0.9.17 2010-11-21 * Converted from Jeweler to Bundler, 'bundle install' to install dependencies diff --git a/Gemfile.lock b/Gemfile.lock index 5d5560f..82ea9c5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,6 +7,7 @@ PATH GEM remote: http://rubygems.org/ specs: + awesome_print (0.3.2) mocha (0.9.12) perftools.rb (0.5.6) rcov (0.9.9) @@ -20,6 +21,7 @@ PLATFORMS DEPENDENCIES amazon-ec2! + awesome_print mocha (>= 0.9.9) perftools.rb (>= 0.5.4) rcov (>= 0.9.9) diff --git a/amazon-ec2.gemspec b/amazon-ec2.gemspec index a892731..97ea938 100644 --- a/amazon-ec2.gemspec +++ b/amazon-ec2.gemspec @@ -28,6 +28,7 @@ Gem::Specification.new do |s| s.add_development_dependency('rcov', '>= 0.9.9') s.add_development_dependency('perftools.rb', '>= 0.5.4') s.add_development_dependency('yard', '>= 0.6.2') + s.add_development_dependency('awesome_print') s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") diff --git a/lib/AWS/EC2.rb b/lib/AWS/EC2.rb index 5850b28..a8daa35 100644 --- a/lib/AWS/EC2.rb +++ b/lib/AWS/EC2.rb @@ -14,7 +14,7 @@ module EC2 DEFAULT_HOST = 'ec2.amazonaws.com' end - API_VERSION = '2010-08-31' + API_VERSION = '2011-02-28' class Base < AWS::Base def api_version diff --git a/lib/AWS/EC2/security_groups.rb b/lib/AWS/EC2/security_groups.rb index 53073f9..f6f8575 100644 --- a/lib/AWS/EC2/security_groups.rb +++ b/lib/AWS/EC2/security_groups.rb @@ -35,10 +35,18 @@ def create_security_group( options = {} ) # returned. If a group is specified that does not exist an exception is returned. # # @option options [optional, Array] :group_name ([]) + # @option options [optional, Array] :group_id ([]) # def describe_security_groups( options = {} ) - options = { :group_name => [] }.merge(options) - params = pathlist("GroupName", options[:group_name] ) + options = { :group_name => [], + :group_id => [] }.merge(options) + params = {} + if options[:group_name] + params.merge!(pathlist("GroupName", options[:group_name])) + end + if options[:group_id] + params.merge!(pathlist("GroupId", options[:group_id])) + end return response_generator(:action => "DescribeSecurityGroups", :params => params) end @@ -74,35 +82,46 @@ def delete_security_group( options = {} ) # GroupName, IpProtocol, FromPort, ToPort and CidrIp must be specified. Mixing these two types # of parameters is not allowed. # - # @option options [String] :group_name ("") - # @option options [optional, String] :ip_protocol (nil) Required when authorizing CIDR IP permission + # @option options [require, String] :ip_protocol (nil) Required when authorizing CIDR IP permission + # @option options [optional, String] :group_id ("") + # @option options [optional, String] :group_name ("") # @option options [optional, Integer] :from_port (nil) Required when authorizing CIDR IP permission # @option options [optional, Integer] :to_port (nil) Required when authorizing CIDR IP permission - # @option options [optional, String] :cidr_ip (nil) Required when authorizing CIDR IP permission - # @option options [optional, String] :source_security_group_name (nil) Required when authorizing user group pair permissions # @option options [optional, String] :source_security_group_user_id (nil) Required when authorizing user group pair permissions + # @option options [optional, String] :source_security_group_name (nil) Required when authorizing user group pair permissions + # @option options [optional, String] :source_security_group_id (nil) ID of the source security group. Cannot be used when specifying a CIDR IP address. For VPC security groups only. Required if modifying access for one or more source security groups. + # @option options [optional, String] :cidr_ip (nil) Required when authorizing CIDR IP permission # def authorize_security_group_ingress( options = {} ) - options = { :group_name => nil, + options = { :group_id => nil, + :group_name => nil, :ip_protocol => nil, :from_port => nil, :to_port => nil, - :cidr_ip => nil, + :source_security_group_user_id => nil, :source_security_group_name => nil, - :source_security_group_user_id => nil }.merge(options) + :source_security_group_id => nil, + :cidr_ip => nil }.merge(options) - # lets not validate the rest of the possible permutations of required params and instead let - # EC2 sort it out on the server side. We'll only require :group_name as that is always needed. - raise ArgumentError, "No :group_name provided" if options[:group_name].nil? || options[:group_name].empty? + if options[:ip_protocol].nil? || options[:ip_protocol].empty? + raise ArgumentError, "No :ip_protocol provided" + end - params = { "GroupName" => options[:group_name], - "IpPermissions.1.IpProtocol" => options[:ip_protocol], - "IpPermissions.1.FromPort" => options[:from_port].to_s, - "IpPermissions.1.ToPort" => options[:to_port].to_s, - "IpPermissions.1.IpRanges.1" => options[:cidr_ip], - "IpPermissions.1.Groups.1.GroupName" => options[:source_security_group_name], - "IpPermissions.1.Groups.1.UserId" => options[:source_security_group_user_id] - } + # lets not validate the rest of the possible permutations of required params and instead let + # EC2 sort it out on the server side. We'll only require :group_id or :group_name as that is always needed. + if (options[:group_id].nil? || options[:group_id].empty?) && (options[:group_name].nil? || options[:group_name].empty?) + raise ArgumentError, "No :group_id or :group_name provided" + end + + params = { "IpPermissions.1.IpProtocol" => options[:ip_protocol] } + params['GroupId'] = options[:group_id] if options[:group_id] + params['GroupName'] = options[:group_name] if options[:group_name] + params["IpPermissions.1.FromPort"] = options[:from_port].to_s if options[:from_port] + params["IpPermissions.1.ToPort"] = options[:to_port].to_s if options[:to_port] + params["IpPermissions.1.Groups.1.UserId"] = options[:source_security_group_user_id] if options[:source_security_group_user_id] + params["IpPermissions.1.Groups.1.GroupName"] = options[:source_security_group_name] if options[:source_security_group_name] + params["IpPermissions.1.Groups.1.GroupId"] = options[:source_security_group_id] if options[:source_security_group_id] + params["IpPermissions.1.IpRanges.1.CidrIp"] = options[:cidr_ip] if options[:cidr_ip] return response_generator(:action => "AuthorizeSecurityGroupIngress", :params => params) end @@ -126,35 +145,46 @@ def authorize_security_group_ingress( options = {} ) # GroupName, IpProtocol, FromPort, ToPort and CidrIp must be specified. Mixing these two types # of parameters is not allowed. # - # @option options [String] :group_name ("") - # @option options [optional, String] :ip_protocol (nil) Required when revoking CIDR IP permission - # @option options [optional, Integer] :from_port (nil) Required when revoking CIDR IP permission - # @option options [optional, Integer] :to_port (nil) Required when revoking CIDR IP permission - # @option options [optional, String] :cidr_ip (nil) Required when revoking CIDR IP permission - # @option options [optional, String] :source_security_group_name (nil) Required when revoking user group pair permissions - # @option options [optional, String] :source_security_group_user_id (nil) Required when revoking user group pair permissions + # @option options [require, String] :ip_protocol (nil) Required when authorizing CIDR IP permission + # @option options [optional, String] :group_id ("") + # @option options [optional, String] :group_name ("") + # @option options [optional, Integer] :from_port (nil) Required when authorizing CIDR IP permission + # @option options [optional, Integer] :to_port (nil) Required when authorizing CIDR IP permission + # @option options [optional, String] :source_security_group_user_id (nil) Required when authorizing user group pair permissions + # @option options [optional, String] :source_security_group_name (nil) Required when authorizing user group pair permissions + # @option options [optional, String] :source_security_group_id (nil) ID of the source security group. Cannot be used when specifying a CIDR IP address. For VPC security groups only. Required if modifying access for one or more source security groups. + # @option options [optional, String] :cidr_ip (nil) Required when authorizing CIDR IP permission # def revoke_security_group_ingress( options = {} ) - options = { :group_name => nil, + options = { :group_id => nil, + :group_name => nil, :ip_protocol => nil, :from_port => nil, :to_port => nil, :cidr_ip => nil, :source_security_group_name => nil, - :source_security_group_user_id => nil }.merge(options) + :source_security_group_user_id => nil, + :source_security_group_id => nil }.merge(options) - # lets not validate the rest of the possible permutations of required params and instead let - # EC2 sort it out on the server side. We'll only require :group_name as that is always needed. - raise ArgumentError, "No :group_name provided" if options[:group_name].nil? || options[:group_name].empty? + if options[:ip_protocol].nil? || options[:ip_protocol].empty? + raise ArgumentError, "No :ip_protocol provided" + end - params = { "GroupName" => options[:group_name], - "IpPermissions.1.IpProtocol" => options[:ip_protocol], - "IpPermissions.1.FromPort" => options[:from_port].to_s, - "IpPermissions.1.ToPort" => options[:to_port].to_s, - "IpPermissions.1.IpRanges.1" => options[:cidr_ip], - "IpPermissions.1.Groups.1.GroupName" => options[:source_security_group_name], - "IpPermissions.1.Groups.1.UserId" => options[:source_security_group_user_id] - } + # lets not validate the rest of the possible permutations of required params and instead let + # EC2 sort it out on the server side. We'll only require :group_id or :group_name as that is always needed. + if (options[:group_id].nil? || options[:group_id].empty?) && (options[:group_name].nil? || options[:group_name].empty?) + raise ArgumentError, "No :group_id or :group_name provided" + end + + params = { "IpPermissions.1.IpProtocol" => options[:ip_protocol] } + params['GroupId'] = options[:group_id] if options[:group_id] + params['GroupName'] = options[:group_name] if options[:group_name] + params["IpPermissions.1.FromPort"] = options[:from_port].to_s if options[:from_port] + params["IpPermissions.1.ToPort"] = options[:to_port].to_s if options[:to_port] + params["IpPermissions.1.Groups.1.UserId"] = options[:source_security_group_user_id] if options[:source_security_group_user_id] + params["IpPermissions.1.Groups.1.GroupName"] = options[:source_security_group_name] if options[:source_security_group_name] + params["IpPermissions.1.Groups.1.GroupId"] = options[:source_security_group_id] if options[:source_security_group_id] + params["IpPermissions.1.IpRanges.1.CidrIp"] = options[:cidr_ip] if options[:cidr_ip] return response_generator(:action => "RevokeSecurityGroupIngress", :params => params) end diff --git a/test/test_EC2_security_groups.rb b/test/test_EC2_security_groups.rb index d44235b..2ae91c2 100644 --- a/test/test_EC2_security_groups.rb +++ b/test/test_EC2_security_groups.rb @@ -162,46 +162,54 @@ specify "permissions should be able to be added to a security group with authorize_security_group_ingress." do - @ec2.stubs(:make_request).with('AuthorizeSecurityGroupIngress', - { "GroupName" => "WebServers", + @ec2.stubs(:make_request).with('AuthorizeSecurityGroupIngress', + { "GroupId" => "sg-00000001", + "GroupName" => "WebServers", "IpPermissions.1.IpProtocol" => "tcp", "IpPermissions.1.FromPort" => "8000", "IpPermissions.1.ToPort" => "80", - "IpPermissions.1.IpRanges.1" => "0.0.0.0/24", - "IpPermissions.1.Groups.1.GroupName" => "Source SG Name", + "IpPermissions.1.IpRanges.1.CidrIp" => "0.0.0.0/24", + "IpPermissions.1.Groups.1.GroupName" => "Source SG Name", + "IpPermissions.1.Groups.1.GroupId" => "456", "IpPermissions.1.Groups.1.UserId" => "123"}). returns stub(:body => @authorize_security_group_ingress_response_body, :is_a? => true) - @ec2.authorize_security_group_ingress( :group_name => "WebServers", + @ec2.authorize_security_group_ingress( :group_id => "sg-00000001", + :group_name => "WebServers", :ip_protocol => "tcp", :from_port => "8000", :to_port => "80", - :cidr_ip => "0.0.0.0/24", + :source_security_group_user_id => "123", :source_security_group_name => "Source SG Name", - :source_security_group_user_id => "123" + :source_security_group_id => "456", + :cidr_ip => "0.0.0.0/24" ).should.be.an.instance_of Hash end specify "permissions should be able to be revoked from a security group with revoke_security_group_ingress." do @ec2.stubs(:make_request).with('RevokeSecurityGroupIngress', - { "GroupName" => "WebServers", + { "GroupId" => "sg-00000001", + "GroupName" => "WebServers", "IpPermissions.1.IpProtocol" => "tcp", "IpPermissions.1.FromPort" => "8000", "IpPermissions.1.ToPort" => "80", - "IpPermissions.1.IpRanges.1" => "0.0.0.0/24", - "IpPermissions.1.Groups.1.GroupName" => "Source SG Name", + "IpPermissions.1.IpRanges.1.CidrIp" => "0.0.0.0/24", + "IpPermissions.1.Groups.1.GroupName" => "Source SG Name", + "IpPermissions.1.Groups.1.GroupId" => "456", "IpPermissions.1.Groups.1.UserId" => "123"}). returns stub(:body => @revoke_security_group_ingress_response_body, :is_a? => true) - @ec2.revoke_security_group_ingress( :group_name => "WebServers", - :ip_protocol => "tcp", - :from_port => "8000", - :to_port => "80", - :cidr_ip => "0.0.0.0/24", - :source_security_group_name => "Source SG Name", - :source_security_group_user_id => "123" - ).should.be.an.instance_of Hash + @ec2.revoke_security_group_ingress( :group_id => "sg-00000001", + :group_name => "WebServers", + :ip_protocol => "tcp", + :from_port => "8000", + :to_port => "80", + :source_security_group_name => "Source SG Name", + :source_security_group_user_id => "123", + :source_security_group_id => "456", + :cidr_ip => "0.0.0.0/24" + ).should.be.an.instance_of Hash end end From 066b9eae60186c1b204dabae7041ae297599644e Mon Sep 17 00:00:00 2001 From: Junya Ogura Date: Tue, 10 May 2011 11:21:17 +0900 Subject: [PATCH 08/24] =?UTF-8?q?=E3=83=89=E3=82=AD=E3=83=A5=E3=83=A1?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=82=92=E6=9B=B4=E6=96=B0=E3=81=97=E3=81=9F?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChangeLog | 2 +- README.md | 26 ++++ README.rdoc | 348 ---------------------------------------------------- 3 files changed, 27 insertions(+), 349 deletions(-) create mode 100644 README.md delete mode 100644 README.rdoc diff --git a/ChangeLog b/ChangeLog index 322d423..89903c1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -=== x.x.x +=== x.x.x 2011-05-08 * add NextToken support to Cloudwatch ListMetrics API. (juno) * added error handling for Cloudwatch. (juno) * added Filters for instances and volumes. (skade) diff --git a/README.md b/README.md new file mode 100644 index 0000000..0057997 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ += Amazon Web Services Elastic Compute Cloud (EC2) Ruby Gem + +コードに変更を加えた場合は`ChangeLog`の最上部に以下のようなエントリを追加します(バージョン番号は`x.x.x`のままでよい)。 + +> === x.x.x 2011-05-08 +> * add NextToken support to Cloudwatch ListMetrics API. (juno) + +変更した`amazon-ec2`をRailsアプリケーションに含める場合は、以下のような手順で行います。 + + $ rake build # => pkg/amazon-ec2-0.9.15.gemが生成される + + $ cd /path/to/rails-app + $ rm -rf vendor/gems/amazon-ec2-0.9.15 # => 古いamazon-ec2ライブラリを削除する + $ gem /path/to/amazon-ec2-0.9.15.gem --target vendor/gems # => 新しいamazon-ec2ライブラリを展開する + $ ls vendor/gems + amazon-ec2-0.9.15 + +`Gemfile`に以下の記述を追加する。 + + gem 'amazon-ec2', :path => 'vendor/gems/amazon-ec2-0.9.15' + +依存gemを更新する。 + + $ bundle update + +この時点で、`vendor/gems`以下および`Gemfile*`をコミットします。 diff --git a/README.rdoc b/README.rdoc deleted file mode 100644 index 74ac5a4..0000000 --- a/README.rdoc +++ /dev/null @@ -1,348 +0,0 @@ -= Amazon Web Services Elastic Compute Cloud (EC2) Ruby Gem - -== About amazon-ec2 - -Amazon Web Services offers a compute power on demand capability known as the Elastic Compute Cloud (EC2). The server resources in the cloud can be provisioned on demand by making HTTP Query API calls to EC2. - -This 'amazon-ec2' Ruby Gem is an interface library that can be used to interact with the Amazon EC2 system and control server resources on demand from your Ruby scripts, or from applications written in your Ruby framework of choice (Ruby on Rails, Merb, etc.). - -More recently, support has been added for the following EC2 related AWS API's as well: - -* Autoscaling -* Cloudwatch -* Elastic Load Balancing (ELB) -* Relational Database Service (RDS) - -For the most complete and up-to date README information please visit the project homepage at: - -http://github.com/grempe/amazon-ec2/tree/master - -or the official EC2 website at http://aws.amazon.com/ec2 - - -== Installation - -This gem follows the standard conventions for installation on any system with Ruby and RubyGems installed and uses Bundler for gem installation and build management. If you have worked with gems before this will look very familiar. - -=== Get an AWS account - -Before you can make use of this gem you will need an Amazon Web Services developer account which you can sign up for at https://aws-portal.amazon.com/gp/aws/developer/registration/index.html. This account must also be specifically enabled for Amazon EC2 usage. AWS will provide you with an 'AWS Access Key ID' and a 'Secret Access Key' which will allow you to authenticate any API calls you make and ensure correct billing to you for usage of the service. Take note of these (and keep them secret!). - -=== Install the amazon-ec2 gem (Canonical Release) - -This is the standard install for stable releases from RubyGems. - - # Install the gem - [sudo] gem install amazon-ec2 - - -=== Install from local Git clone (for amazon-ec2 developers) - -To install from git for adding features or fixing bugs, you'll need to clone and build. - - git clone git://github.com/grempe/amazon-ec2.git - cd amazon-ec2 - bundle install - rake test - rake build - rake install - - -== Using amazon-ec2 - -The library exposes one main interface class AWS::EC2::Base. It is through an instance of this class that you will perform all the operations for using the EC2 service including query string header signing. - -The public methods on AWS::EC2::Base closely mirror the EC2 Query API, and as such the Query API Reference in the EC2 Developer Guide ( http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=84 ) will prove helpful. - - -=== Setting up - -The 'awshell' and 'ec2-gem-example.rb' scripts which will be introduced to you shortly expect your AWS EC2 credentials to be stored as shell environment variables which are accessible to those scripts. This makes them convenient to use whenever you need to do a quick query to see what images you have available to you, what's running now, or to start or stop an instance on EC2. You'll find 'awshell' to be a very handy tool. I'll describe only the OS X route for setting up (of course the setup steps will vary depending on your particular system and preferred shell). If you don't want to do it this way, feel free to copy these scripts from the gem dir to any location where you can run them from and modify them directly to include your credentials. - -Edit the file ~/.bash_login and add the following to the existing contents: - - # OPTIONAL : Why 'export RUBYOPT'? Because we leave loading libs up to you... See : http://gist.github.com/54177 - export RUBYOPT="rubygems" - - # For amazon-ec2 and amazon s3 ruby gems - export AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY_ID" - export AWS_SECRET_ACCESS_KEY="YOUR_SECRET_ACCESS_KEY_ID" - -If you are using EC2 in the EU region, make sure you also set: - - export EC2_URL="https://eu-west-1.ec2.amazonaws.com" - -(which you have already if you configured standard EC2 command line tools to work with this region). - -Once you save the file you should close and re-open your terminal so the new variables are made available. You'll need to do this close/re-open step for each terminal window you have open (or issue the 'source ~/.bash_login' command in each). Make sure that this file is only readable by your user so you don't inadvertently expose your credentials to other users on your system. - -You can verify that this setup is complete by running the 'set' in a command window and seeing that your credentials are in the list of shell variables. - -=== The basics - -The library exposes one main interface module - - AWS::EC2::Base - -This method requires arguments which include your AWS credentials and it will return an object that you can use to make method calls directly against EC2. All the operations for using the EC2 service, including query string header signing, are handled automatically for you. The connection string will look something like this: - - @ec2 = AWS::EC2::Base.new(:access_key_id => ACCESS_KEY_ID, :secret_access_key => SECRET_ACCESS_KEY) - -I've tried to keep the public methods on 'amazon-ec2' as close as possible to the AWS EC2 Query API. This similarity allows you to reference the Query API Reference in the EC2 Developer Guide and be able to get started right away. In most cases the methods names only differ in how they are presented. e.g. 'DescribeImages' becomes '#describe_images()' in Ruby. Feel free to browse the full RDoc documentation for all classes and methods of 'amazon-ec2' if you want more details. - - -=== Examples - -The best way to become familiar with 'amazon-ec2' is to take it for a test drive. We have provided a few simple ways to get you started. There is also some sample code below that should help out in using 'amazon-ec2' with a plain Ruby script, or as part of a Ruby on Rails application. - -==== Using the 'ec2-gem-example.rb' sample test script - -An example Ruby script which exercises the library a bit more is installed for you to check out when you install this gem. You can run this script to verify that everything is setup and working correctly in your environment. Consult the file which is installed at : - - [your amazon-ec2 gem dir]/examples/ec2-example.rb - -Since we also package this sample file in the gem's bin/ dir you should also be able to run it from anywhere on your shell path (once you have set your environment variables as described above). - -==== Using the 'awshell' command shell - -The 'awshell' command shell is actually a standard 'irb' Ruby shell, with the main difference being we read your AWS credentials from your environment and pre-configure a connection string for you. This lets you run any EC2 command very simply. This has proven to be a valuable tool during the development of this gem and you should try it out. Since we install this tool in your system path as part of the installation of this gem, you should be able to simply run 'awshell' from any terminal command prompt on your local system. You'll see some basic instructions for use, and a few examples when you start 'awshell'. Go ahead and try it out now. We'll wait... - -If you're not in front of a terminal shell now (perhaps you're browsing this site on your iPhone) this is what you would see: - - - hostname:/tmp/rails/amazon_test glenn$ awshell - - 'awshell' usage : - This is an interactive 'irb' command shell that allows you to use all - commands available to the amazon-ec2 gem. You'll find this to be a - great tool to help you debug issues and practice running commands - against the live EC2 servers prior to putting them in your code. - - The EC2 connection is wired to the class instance '@ec2'. Make method calls - on this to execute commands on EC2. Adding a #to_s - at the end of any command should give you a full String representation of the - response. - - Examples to try: - - returns : all ec2 public methods - >> @ec2.methods.sort - - returns : a string representation of ALL images - >> @ec2.describe_images.to_s - - returns : an Array of AWS::Response objects, each an EC2 image and its data - >> @ec2.describe_images.imagesSet.item - >> @ec2.describe_images.imagesSet.item[0] (a hash representing a single item in that array) - >> @ec2.describe_images.imagesSet.item[0].to_s (a String representation of that item) - - >> @ec2.describe_images.imagesSet.item[0].to_s - => "#>" - - -=== Ruby script usage example: - -Try out the following bit of code. This should walk through each image returned by a call to #describe_images and print out its key data. Note in the example below that you cannot walk through the results of the #describe_images call with the '.each' iterator (You'll get errors if you try). You need to instead walk through the Array of items which are in the 'imagesSet' embedded in the response. This reflects exactly the XML hierarchy of data returned from EC2 which we parse to Ruby OpenStruct objects (AWS::Response). - - #!/usr/bin/env ruby - - require 'rubygems' - require 'AWS' - - ACCESS_KEY_ID = '--YOUR AWS ACCESS KEY ID--' - SECRET_ACCESS_KEY = '--YOUR AWS SECRET ACCESS KEY--' - - ec2 = AWS::EC2::Base.new(:access_key_id => ACCESS_KEY_ID, :secret_access_key => SECRET_ACCESS_KEY) - - puts "----- listing images owned by 'amazon' -----" - ec2.describe_images(:owner_id => "amazon").imagesSet.item.each do |image| - # OpenStruct objects have members! - image.members.each do |member| - puts "#{member} => #{image[member]}" - end - end - -=== Ruby on Rails usage example: - - -Rails 2.3.x - config/environment.rb - - Rails::Initializer.run do |config| - ... - config.gem "amazon-ec2", :lib => "AWS", :source => "http://gemcutter.org/" - ... - end - - -Rails 3.x.x - Gemfile - - ... - gem "amazon-ec2", :require => "AWS" - - -app/controllers/my_controller.rb - - [some controller code ...] - - ec2 = AWS::EC2::Base.new(:access_key_id => "YOUR_AWS_ACCESS_KEY_ID", :secret_access_key => "YOUR_AWS_SECRET_ACCESS_KEY") - - # get ALL public images - @ec2_images = ec2.describe_images().imagesSet.item - - # Get info on all public EC2 images created by the Amazon EC2 team. - @ec2_images_amazon = ec2.describe_images(:owner_id => "amazon").imagesSet.item - - [some more controller code ...] - - -app/views/my/index.rhtml - -

EC2 Test#index

- -

Sample 1 - debug() view

- - <%= debug(@ec2_images_amazon) %> - -

Sample 2 - Build a table

- - - - - - - - - - - <% for image in @ec2_images_amazon %> - - - - - - - - <% end %> -
image.imageIdimage.imageLocationimage.imageOwnerIdimage.imageStateimage.isPublic
<%=h image.imageId %><%=h image.imageLocation %><%=h image.imageOwnerId %><%=h image.imageState %><%=h image.isPublic %>
- -

Sample 3 - Iterate

- - <% @ec2_images_amazon.each do |image| %> - <% image.each_pair do |key, value| %> - <% unless key == 'parent' %> - <%= "#{key} => #{value}" %>
- <% end %> - <% end %> -
- <% end %> - - -=== Important notes regarding the structure of AWS::Response Objects - -One of the key benefits of this new version of the library is that all responses from EC2 are bundled up in a real data structure and no longer require parsing of text. The hash returned is populated directly from the XML given to us by EC2 in response to any command we issue. This means that future changes to the API and what is returned by EC2 will be handled transparently by the gem. This is a huge benefit. What this means though, is that you may have to do a little homework on what actually gets returned by EC2 as XML. For example, when you make a #describe_images call in 'awshell' what AWS returns behind the scenes looks like: - - - - - - ami-20b65349 - ec2-public-images/fedora-core4-base.manifest.xml - available - amazon - true - - - ami-22b6534b - ec2-public-images/fedora-core4-mysql.manifest.xml - available - amazon - true - - - ami-23b6534a - ec2-public-images/fedora-core4-apache.manifest.xml - available - amazon - true - - - ami-25b6534c - ec2-public-images/fedora-core4-apache-mysql.manifest.xml - available - amazon - true - - - ami-26b6534f - ec2-public-images/developer-image.manifest.xml - available - amazon - true - - - ami-2bb65342 - ec2-public-images/getting-started.manifest.xml - available - amazon - true - - - - -You can see here the XML the structure that you will need to follow when constructing queries for information and parsing responses from EC2. - -So, for example, if you wanted to get the image ID of the third image listed in the response above you would need to do: - - >> puts @ec2.describe_images(:owner_id => 'amazon').imagesSet.item[2].imageId - ami-23b6534a - -EC2 will typically return sets of things (imagesSet, reservationSet, etc.) which we map to ruby Arrays (.imagesSet.item in the example above). If you want to iterate over a response set you will need to iterate over this array. The Arrays will typically contain additional AWS::Response objects that represent each individual item. You'll find that you can use the 'awshell' to help you understand the structure more completely if you try issuing commands there as a way to practice seeing what will be returned and making sure you get exactly what you want. - -=== Handling Exceptions -If for some reason an error occurs when executing a method (e.g. its arguments were -incorrect, or it simply failed) then an exception will be thrown. The exceptions are -defined in exceptions.rb as individual classes and should match the exceptions that -AWS has defined in the API. If the exception raised cannot be identified then a -more generic exception class will be thrown. - -The implication of this is that you need to be prepared to handle any exceptions that -may be raised by this library in YOUR code with a 'rescue' clause. It is up to you -to determine how you want to handle these exceptions in your code. - - -== Additional Resources - -=== Project Websites - -* Project Home : http://github.com/grempe/amazon-ec2/tree/master -* API Documentation : http://rdoc.info/projects/grempe/amazon-ec2 -* Discussion Group : http://groups.google.com/group/amazon-ec2 -* Report Bugs / Request Features : http://github.com/grempe/amazon-ec2/issues -* Amazon Web Services : http://aws.amazon.com - -== Credits - -The original code for this library was provided by Amazon Web Services, LLC as sample code. Thanks to them for providing all of us with something to get us started. - -== Contact - -Comments, patches, Git pull requests and bug reports are welcome. Send an email to mailto:glenn@rempe.us or join the Google Groups forum. - -== Patches & Pull Requests - -Please follow these steps if you want to send a patch or a GitHub pull request: - -* Fork grempe/amazon-ec2 -* Create a topic branch: `git checkout -b my_fix` -* Make sure you add tests for your changes and that they all pass with 'rake test' -* Don't change files that you don't own like the gemspec or version.rb -* Commit your changes, one change/fix per commit -* Push your fixes branch: `git push origin my_fix` -* Open an Issue on GitHub referencing your branch and send a pull request. -* Please do not push to `master` on your fork. Using a feature/bugfix branch will make everyone’s life easier. - - -Enjoy! - -Glenn Rempe - From 54a53e2c30f7f9b35fd520a4c0d8158220815557 Mon Sep 17 00:00:00 2001 From: Junya Ogura Date: Tue, 10 May 2011 11:22:25 +0900 Subject: [PATCH 09/24] update. --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0057997..5339131 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ -= Amazon Web Services Elastic Compute Cloud (EC2) Ruby Gem +amazon-ec2カスタマイズ版 +==== コードに変更を加えた場合は`ChangeLog`の最上部に以下のようなエントリを追加します(バージョン番号は`x.x.x`のままでよい)。 -> === x.x.x 2011-05-08 -> * add NextToken support to Cloudwatch ListMetrics API. (juno) + === x.x.x 2011-05-08 + * add NextToken support to Cloudwatch ListMetrics API. (juno) 変更した`amazon-ec2`をRailsアプリケーションに含める場合は、以下のような手順で行います。 From 631c2a1f9da5bd4e34f7b55d07ce118072ebf00a Mon Sep 17 00:00:00 2001 From: Junya Ogura Date: Tue, 10 May 2011 18:01:16 +0900 Subject: [PATCH 10/24] updated --- README.rdoc | 347 ++++++++++++++++++++++++++++++++++++++++++++++++++++ VERSION | 2 +- 2 files changed, 348 insertions(+), 1 deletion(-) create mode 100644 README.rdoc diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 0000000..9d09db7 --- /dev/null +++ b/README.rdoc @@ -0,0 +1,347 @@ += Amazon Web Services Elastic Compute Cloud (EC2) Ruby Gem + +== About amazon-ec2 + +Amazon Web Services offers a compute power on demand capability known as the Elastic Compute Cloud (EC2). The server resources in the cloud can be provisioned on demand by making HTTP Query API calls to EC2. + +This 'amazon-ec2' Ruby Gem is an interface library that can be used to interact with the Amazon EC2 system and control server resources on demand from your Ruby scripts, or from applications written in your Ruby framework of choice (Ruby on Rails, Merb, etc.). + +More recently, support has been added for the following EC2 related AWS API's as well: + +* Autoscaling +* Cloudwatch +* Elastic Load Balancing (ELB) +* Relational Database Service (RDS) + +For the most complete and up-to date README information please visit the project homepage at: + +http://github.com/grempe/amazon-ec2/tree/master + +or the official EC2 website at http://aws.amazon.com/ec2 + + +== Installation + +This gem follows the standard conventions for installation on any system with Ruby and RubyGems installed and uses Bundler for gem installation and build management. If you have worked with gems before this will look very familiar. + +=== Get an AWS account + +Before you can make use of this gem you will need an Amazon Web Services developer account which you can sign up for at https://aws-portal.amazon.com/gp/aws/developer/registration/index.html. This account must also be specifically enabled for Amazon EC2 usage. AWS will provide you with an 'AWS Access Key ID' and a 'Secret Access Key' which will allow you to authenticate any API calls you make and ensure correct billing to you for usage of the service. Take note of these (and keep them secret!). + +=== Install the amazon-ec2 gem (Canonical Release) + +This is the standard install for stable releases from RubyGems. + + # Install the gem + [sudo] gem install amazon-ec2 + + +=== Install from local Git clone (for amazon-ec2 developers) + +To install from git for adding features or fixing bugs, you'll need to clone and build. + + git clone git://github.com/grempe/amazon-ec2.git + cd amazon-ec2 + bundle install + rake test + rake build + rake install + + +== Using amazon-ec2 + +The library exposes one main interface class AWS::EC2::Base. It is through an instance of this class that you will perform all the operations for using the EC2 service including query string header signing. + +The public methods on AWS::EC2::Base closely mirror the EC2 Query API, and as such the Query API Reference in the EC2 Developer Guide ( http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=84 ) will prove helpful. + + +=== Setting up + +The 'awshell' and 'ec2-gem-example.rb' scripts which will be introduced to you shortly expect your AWS EC2 credentials to be stored as shell environment variables which are accessible to those scripts. This makes them convenient to use whenever you need to do a quick query to see what images you have available to you, what's running now, or to start or stop an instance on EC2. You'll find 'awshell' to be a very handy tool. I'll describe only the OS X route for setting up (of course the setup steps will vary depending on your particular system and preferred shell). If you don't want to do it this way, feel free to copy these scripts from the gem dir to any location where you can run them from and modify them directly to include your credentials. + +Edit the file ~/.bash_login and add the following to the existing contents: + + # OPTIONAL : Why 'export RUBYOPT'? Because we leave loading libs up to you... See : http://gist.github.com/54177 + export RUBYOPT="rubygems" + + # For amazon-ec2 and amazon s3 ruby gems + export AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY_ID" + export AWS_SECRET_ACCESS_KEY="YOUR_SECRET_ACCESS_KEY_ID" + +If you are using EC2 in the EU region, make sure you also set: + + export EC2_URL="https://eu-west-1.ec2.amazonaws.com" + +(which you have already if you configured standard EC2 command line tools to work with this region). + +Once you save the file you should close and re-open your terminal so the new variables are made available. You'll need to do this close/re-open step for each terminal window you have open (or issue the 'source ~/.bash_login' command in each). Make sure that this file is only readable by your user so you don't inadvertently expose your credentials to other users on your system. + +You can verify that this setup is complete by running the 'set' in a command window and seeing that your credentials are in the list of shell variables. + +=== The basics + +The library exposes one main interface module + + AWS::EC2::Base + +This method requires arguments which include your AWS credentials and it will return an object that you can use to make method calls directly against EC2. All the operations for using the EC2 service, including query string header signing, are handled automatically for you. The connection string will look something like this: + + @ec2 = AWS::EC2::Base.new(:access_key_id => ACCESS_KEY_ID, :secret_access_key => SECRET_ACCESS_KEY) + +I've tried to keep the public methods on 'amazon-ec2' as close as possible to the AWS EC2 Query API. This similarity allows you to reference the Query API Reference in the EC2 Developer Guide and be able to get started right away. In most cases the methods names only differ in how they are presented. e.g. 'DescribeImages' becomes '#describe_images()' in Ruby. Feel free to browse the full RDoc documentation for all classes and methods of 'amazon-ec2' if you want more details. + + +=== Examples + +The best way to become familiar with 'amazon-ec2' is to take it for a test drive. We have provided a few simple ways to get you started. There is also some sample code below that should help out in using 'amazon-ec2' with a plain Ruby script, or as part of a Ruby on Rails application. + +==== Using the 'ec2-gem-example.rb' sample test script + +An example Ruby script which exercises the library a bit more is installed for you to check out when you install this gem. You can run this script to verify that everything is setup and working correctly in your environment. Consult the file which is installed at : + + [your amazon-ec2 gem dir]/examples/ec2-example.rb + +Since we also package this sample file in the gem's bin/ dir you should also be able to run it from anywhere on your shell path (once you have set your environment variables as described above). + +==== Using the 'awshell' command shell + +The 'awshell' command shell is actually a standard 'irb' Ruby shell, with the main difference being we read your AWS credentials from your environment and pre-configure a connection string for you. This lets you run any EC2 command very simply. This has proven to be a valuable tool during the development of this gem and you should try it out. Since we install this tool in your system path as part of the installation of this gem, you should be able to simply run 'awshell' from any terminal command prompt on your local system. You'll see some basic instructions for use, and a few examples when you start 'awshell'. Go ahead and try it out now. We'll wait... + +If you're not in front of a terminal shell now (perhaps you're browsing this site on your iPhone) this is what you would see: + + + hostname:/tmp/rails/amazon_test glenn$ awshell + + 'awshell' usage : + This is an interactive 'irb' command shell that allows you to use all + commands available to the amazon-ec2 gem. You'll find this to be a + great tool to help you debug issues and practice running commands + against the live EC2 servers prior to putting them in your code. + + The EC2 connection is wired to the class instance '@ec2'. Make method calls + on this to execute commands on EC2. Adding a #to_s + at the end of any command should give you a full String representation of the + response. + + Examples to try: + + returns : all ec2 public methods + >> @ec2.methods.sort + + returns : a string representation of ALL images + >> @ec2.describe_images.to_s + + returns : an Array of AWS::Response objects, each an EC2 image and its data + >> @ec2.describe_images.imagesSet.item + >> @ec2.describe_images.imagesSet.item[0] (a hash representing a single item in that array) + >> @ec2.describe_images.imagesSet.item[0].to_s (a String representation of that item) + + >> @ec2.describe_images.imagesSet.item[0].to_s + => "#>" + + +=== Ruby script usage example: + +Try out the following bit of code. This should walk through each image returned by a call to #describe_images and print out its key data. Note in the example below that you cannot walk through the results of the #describe_images call with the '.each' iterator (You'll get errors if you try). You need to instead walk through the Array of items which are in the 'imagesSet' embedded in the response. This reflects exactly the XML hierarchy of data returned from EC2 which we parse to Ruby OpenStruct objects (AWS::Response). + + #!/usr/bin/env ruby + + require 'rubygems' + require 'AWS' + + ACCESS_KEY_ID = '--YOUR AWS ACCESS KEY ID--' + SECRET_ACCESS_KEY = '--YOUR AWS SECRET ACCESS KEY--' + + ec2 = AWS::EC2::Base.new(:access_key_id => ACCESS_KEY_ID, :secret_access_key => SECRET_ACCESS_KEY) + + puts "----- listing images owned by 'amazon' -----" + ec2.describe_images(:owner_id => "amazon").imagesSet.item.each do |image| + # OpenStruct objects have members! + image.members.each do |member| + puts "#{member} => #{image[member]}" + end + end + +=== Ruby on Rails usage example: + + +Rails 2.3.x - config/environment.rb + + Rails::Initializer.run do |config| + ... + config.gem "amazon-ec2", :lib => "AWS", :source => "http://gemcutter.org/" + ... + end + + +Rails 3.x.x - Gemfile + + ... + gem "amazon-ec2", :require => "AWS" + + +app/controllers/my_controller.rb + + [some controller code ...] + + ec2 = AWS::EC2::Base.new(:access_key_id => "YOUR_AWS_ACCESS_KEY_ID", :secret_access_key => "YOUR_AWS_SECRET_ACCESS_KEY") + + # get ALL public images + @ec2_images = ec2.describe_images().imagesSet.item + + # Get info on all public EC2 images created by the Amazon EC2 team. + @ec2_images_amazon = ec2.describe_images(:owner_id => "amazon").imagesSet.item + + [some more controller code ...] + + +app/views/my/index.rhtml + +

EC2 Test#index

+ +

Sample 1 - debug() view

+ + <%= debug(@ec2_images_amazon) %> + +

Sample 2 - Build a table

+ + + + + + + + + + + <% for image in @ec2_images_amazon %> + + + + + + + + <% end %> +
image.imageIdimage.imageLocationimage.imageOwnerIdimage.imageStateimage.isPublic
<%=h image.imageId %><%=h image.imageLocation %><%=h image.imageOwnerId %><%=h image.imageState %><%=h image.isPublic %>
+ +

Sample 3 - Iterate

+ + <% @ec2_images_amazon.each do |image| %> + <% image.each_pair do |key, value| %> + <% unless key == 'parent' %> + <%= "#{key} => #{value}" %>
+ <% end %> + <% end %> +
+ <% end %> + + +=== Important notes regarding the structure of AWS::Response Objects + +One of the key benefits of this new version of the library is that all responses from EC2 are bundled up in a real data structure and no longer require parsing of text. The hash returned is populated directly from the XML given to us by EC2 in response to any command we issue. This means that future changes to the API and what is returned by EC2 will be handled transparently by the gem. This is a huge benefit. What this means though, is that you may have to do a little homework on what actually gets returned by EC2 as XML. For example, when you make a #describe_images call in 'awshell' what AWS returns behind the scenes looks like: + + + + + + ami-20b65349 + ec2-public-images/fedora-core4-base.manifest.xml + available + amazon + true + + + ami-22b6534b + ec2-public-images/fedora-core4-mysql.manifest.xml + available + amazon + true + + + ami-23b6534a + ec2-public-images/fedora-core4-apache.manifest.xml + available + amazon + true + + + ami-25b6534c + ec2-public-images/fedora-core4-apache-mysql.manifest.xml + available + amazon + true + + + ami-26b6534f + ec2-public-images/developer-image.manifest.xml + available + amazon + true + + + ami-2bb65342 + ec2-public-images/getting-started.manifest.xml + available + amazon + true + + + + +You can see here the XML the structure that you will need to follow when constructing queries for information and parsing responses from EC2. + +So, for example, if you wanted to get the image ID of the third image listed in the response above you would need to do: + + >> puts @ec2.describe_images(:owner_id => 'amazon').imagesSet.item[2].imageId + ami-23b6534a + +EC2 will typically return sets of things (imagesSet, reservationSet, etc.) which we map to ruby Arrays (.imagesSet.item in the example above). If you want to iterate over a response set you will need to iterate over this array. The Arrays will typically contain additional AWS::Response objects that represent each individual item. You'll find that you can use the 'awshell' to help you understand the structure more completely if you try issuing commands there as a way to practice seeing what will be returned and making sure you get exactly what you want. + +=== Handling Exceptions +If for some reason an error occurs when executing a method (e.g. its arguments were +incorrect, or it simply failed) then an exception will be thrown. The exceptions are +defined in exceptions.rb as individual classes and should match the exceptions that +AWS has defined in the API. If the exception raised cannot be identified then a +more generic exception class will be thrown. + +The implication of this is that you need to be prepared to handle any exceptions that +may be raised by this library in YOUR code with a 'rescue' clause. It is up to you +to determine how you want to handle these exceptions in your code. + + +== Additional Resources + +=== Project Websites + +* Project Home : http://github.com/grempe/amazon-ec2/tree/master +* API Documentation : http://rdoc.info/projects/grempe/amazon-ec2 +* Discussion Group : http://groups.google.com/group/amazon-ec2 +* Report Bugs / Request Features : http://github.com/grempe/amazon-ec2/issues +* Amazon Web Services : http://aws.amazon.com + +== Credits + +The original code for this library was provided by Amazon Web Services, LLC as sample code. Thanks to them for providing all of us with something to get us started. + +== Contact + +Comments, patches, Git pull requests and bug reports are welcome. Send an email to mailto:glenn@rempe.us or join the Google Groups forum. + +== Patches & Pull Requests + +Please follow these steps if you want to send a patch or a GitHub pull request: + +* Fork grempe/amazon-ec2 +* Create a topic branch: `git checkout -b my_fix` +* Make sure you add tests for your changes and that they all pass with 'rake test' +* Don't change files that you don't own like the gemspec or version.rb +* Commit your changes, one change/fix per commit +* Push your fixes branch: `git push origin my_fix` +* Open an Issue on GitHub referencing your branch and send a pull request. +* Please do not push to `master` on your fork. Using a feature/bugfix branch will make everyone’s life easier. + + +Enjoy! + +Glenn Rempe diff --git a/VERSION b/VERSION index 5d11b14..4148992 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.15 +0.9.17 From fc863d389b9e258778ca6daa469dcfb6be005716 Mon Sep 17 00:00:00 2001 From: Norihito Yoshioka Date: Wed, 25 May 2011 20:40:01 +0900 Subject: [PATCH 11/24] =?UTF-8?q?HTTP=20=E3=83=AA=E3=82=AF=E3=82=A8?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=81=AE=20Content-Type=20=E3=83=98=E3=83=83?= =?UTF-8?q?=E3=83=80=E3=83=BC=E3=81=AB=20charset=3Dutf-8=20=E3=82=92?= =?UTF-8?q?=E4=BB=98=E5=8A=A0=E3=81=97=E3=81=9F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/AWS.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/AWS.rb b/lib/AWS.rb index 21c77d9..eea2a94 100644 --- a/lib/AWS.rb +++ b/lib/AWS.rb @@ -291,7 +291,7 @@ def make_request(action, params, data='') end.join("&") + "&Signature=" + sig req = Net::HTTP::Post.new(@path) - req.content_type = 'application/x-www-form-urlencoded' + req.content_type = 'application/x-www-form-urlencoded; charset=utf-8' req['User-Agent'] = "github-amazon-ec2-ruby-gem" response = @http.request(req, query) From 97ab6a6347c42c0d714a46e7a97039053c26fd4c Mon Sep 17 00:00:00 2001 From: Norihito Yoshioka Date: Thu, 26 May 2011 11:05:11 +0900 Subject: [PATCH 12/24] Fix a require problem in the Ruby 1.9. --- Gemfile.lock | 1 + test/test_Autoscaling_groups.rb | 2 +- test/test_Cloudwatch.rb | 2 +- test/test_EC2.rb | 2 +- test/test_EC2_availability_zones.rb | 2 +- test/test_EC2_console.rb | 2 +- test/test_EC2_elastic_ips.rb | 2 +- test/test_EC2_image_attributes.rb | 2 +- test/test_EC2_images.rb | 2 +- test/test_EC2_instances.rb | 2 +- test/test_EC2_keypairs.rb | 2 +- test/test_EC2_password.rb | 2 +- test/test_EC2_products.rb | 4 ++-- test/test_EC2_responses.rb | 2 +- test/test_EC2_s3_xmlsimple.rb | 2 +- test/test_EC2_security_groups.rb | 2 +- test/test_EC2_snapshots.rb | 2 +- test/test_EC2_spot_instance_requests.rb | 2 +- test/test_EC2_spot_prices.rb | 2 +- test/test_EC2_subnets.rb | 2 +- test/test_EC2_volumes.rb | 2 +- test/test_ELB_load_balancers.rb | 2 +- test/test_RDS.rb | 2 +- test/test_helper.rb | 2 +- 24 files changed, 25 insertions(+), 24 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 82ea9c5..b792133 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -27,4 +27,5 @@ DEPENDENCIES rcov (>= 0.9.9) test-spec (>= 0.10.0) test-unit (>= 2.1.2) + xml-simple (>= 1.0.12) yard (>= 0.6.2) diff --git a/test/test_Autoscaling_groups.rb b/test/test_Autoscaling_groups.rb index 70ce3e1..b456f15 100644 --- a/test/test_Autoscaling_groups.rb +++ b/test/test_Autoscaling_groups.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "autoscaling " do before do diff --git a/test/test_Cloudwatch.rb b/test/test_Cloudwatch.rb index 9726510..5a5ec46 100644 --- a/test/test_Cloudwatch.rb +++ b/test/test_Cloudwatch.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "cloudwatch " do before do diff --git a/test/test_EC2.rb b/test/test_EC2.rb index 96d4cc9..affa1fc 100644 --- a/test/test_EC2.rb +++ b/test/test_EC2.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "The EC2 method " do diff --git a/test/test_EC2_availability_zones.rb b/test/test_EC2_availability_zones.rb index 2c3ae7d..01eecf0 100644 --- a/test/test_EC2_availability_zones.rb +++ b/test/test_EC2_availability_zones.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "EC2 availability zones" do diff --git a/test/test_EC2_console.rb b/test/test_EC2_console.rb index 00b16ec..450a1f6 100644 --- a/test/test_EC2_console.rb +++ b/test/test_EC2_console.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "The EC2 console " do diff --git a/test/test_EC2_elastic_ips.rb b/test/test_EC2_elastic_ips.rb index 337f135..cf9d201 100644 --- a/test/test_EC2_elastic_ips.rb +++ b/test/test_EC2_elastic_ips.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "EC2 elastic IP addresses " do diff --git a/test/test_EC2_image_attributes.rb b/test/test_EC2_image_attributes.rb index 3946c90..a6caf0c 100644 --- a/test/test_EC2_image_attributes.rb +++ b/test/test_EC2_image_attributes.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "EC2 image_attributes " do diff --git a/test/test_EC2_images.rb b/test/test_EC2_images.rb index 8ca481f..c356ef2 100644 --- a/test/test_EC2_images.rb +++ b/test/test_EC2_images.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "An EC2 image " do diff --git a/test/test_EC2_instances.rb b/test/test_EC2_instances.rb index 2d771d4..7660f58 100644 --- a/test/test_EC2_instances.rb +++ b/test/test_EC2_instances.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "EC2 instances " do diff --git a/test/test_EC2_keypairs.rb b/test/test_EC2_keypairs.rb index fe6d2dc..e1bedfe 100644 --- a/test/test_EC2_keypairs.rb +++ b/test/test_EC2_keypairs.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "EC2 keypairs " do diff --git a/test/test_EC2_password.rb b/test/test_EC2_password.rb index 64cad38..969fb7a 100644 --- a/test/test_EC2_password.rb +++ b/test/test_EC2_password.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "The EC2 password " do diff --git a/test/test_EC2_products.rb b/test/test_EC2_products.rb index bf7c178..a198516 100644 --- a/test/test_EC2_products.rb +++ b/test/test_EC2_products.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "An EC2 instance " do @@ -45,4 +45,4 @@ end -end \ No newline at end of file +end diff --git a/test/test_EC2_responses.rb b/test/test_EC2_responses.rb index 999c4b8..098a266 100644 --- a/test/test_EC2_responses.rb +++ b/test/test_EC2_responses.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "The Response classes " do diff --git a/test/test_EC2_s3_xmlsimple.rb b/test/test_EC2_s3_xmlsimple.rb index 4ebe156..c31ed0c 100644 --- a/test/test_EC2_s3_xmlsimple.rb +++ b/test/test_EC2_s3_xmlsimple.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' # NOTE : These tests exercise amazon-ec2 when used with the aws/s3 gem # which was demonstrating some breaking behavior. The fix was to diff --git a/test/test_EC2_security_groups.rb b/test/test_EC2_security_groups.rb index 2ae91c2..b25fe99 100644 --- a/test/test_EC2_security_groups.rb +++ b/test/test_EC2_security_groups.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "EC2 security groups " do diff --git a/test/test_EC2_snapshots.rb b/test/test_EC2_snapshots.rb index 1a13513..7cefd1d 100644 --- a/test/test_EC2_snapshots.rb +++ b/test/test_EC2_snapshots.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "EC2 snaphots " do diff --git a/test/test_EC2_spot_instance_requests.rb b/test/test_EC2_spot_instance_requests.rb index c751ac0..2d361ae 100644 --- a/test/test_EC2_spot_instance_requests.rb +++ b/test/test_EC2_spot_instance_requests.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "An EC2 spot instances request " do diff --git a/test/test_EC2_spot_prices.rb b/test/test_EC2_spot_prices.rb index 485d885..71ffd3a 100644 --- a/test/test_EC2_spot_prices.rb +++ b/test/test_EC2_spot_prices.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "Spot price history " do diff --git a/test/test_EC2_subnets.rb b/test/test_EC2_subnets.rb index 19ece5e..949ae1c 100644 --- a/test/test_EC2_subnets.rb +++ b/test/test_EC2_subnets.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "The EC2 subnets " do diff --git a/test/test_EC2_volumes.rb b/test/test_EC2_volumes.rb index c118ca1..08e40f5 100644 --- a/test/test_EC2_volumes.rb +++ b/test/test_EC2_volumes.rb @@ -8,7 +8,7 @@ # Home:: http://github.com/grempe/amazon-ec2/tree/master #++ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "EC2 volumes " do diff --git a/test/test_ELB_load_balancers.rb b/test/test_ELB_load_balancers.rb index 48fa12d..7c0c166 100644 --- a/test/test_ELB_load_balancers.rb +++ b/test/test_ELB_load_balancers.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "elb load balancers " do before do diff --git a/test/test_RDS.rb b/test/test_RDS.rb index 083071d..0ba0013 100644 --- a/test/test_RDS.rb +++ b/test/test_RDS.rb @@ -1,4 +1,4 @@ -require File.dirname(__FILE__) + '/test_helper.rb' +require File.expand_path(File.dirname(__FILE__)) + '/test_helper.rb' context "rds databases " do before do diff --git a/test/test_helper.rb b/test/test_helper.rb index 14562e1..d0822e0 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -19,5 +19,5 @@ end } -require File.dirname(__FILE__) + '/../lib/AWS' +require File.expand_path(File.dirname(__FILE__)) + '/../lib/AWS' From 525e71d9fd87b141042dd43679b7bf2590f8257f Mon Sep 17 00:00:00 2001 From: Norihito Yoshioka Date: Thu, 26 May 2011 12:01:29 +0900 Subject: [PATCH 13/24] Implement the filterlist method. --- lib/AWS.rb | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/lib/AWS.rb b/lib/AWS.rb index eea2a94..748f304 100644 --- a/lib/AWS.rb +++ b/lib/AWS.rb @@ -267,6 +267,50 @@ def pathkvlist(key, arr_of_hashes, key_name, value_name, mappings) params end + # Same as _pathhashlist_ except it generates explicit Filter..Name and Filter..Value. + # depending on whether the value is a scalar or an array. + # + # So if you pass in arg + # (:name=>'jon', :names=>['chris', 'bob']), + # + # you should get + # {"Filter.1.Name"=>"name", "Filter.1.Value.1"=>'jon', "Filter.2.Name"=>'names', 'Filter.2.Value.1'=>'chris', 'Filter.2.Value.2'=>'bob'} + # or + # {"Filter.1.Name"=>'names', 'Filter.1.Value.1'=>'chris', 'Filter.1.Value.2'=>'bob', "Filter.1.Name"=>"name", "Filter.2.Value.1"=>'jon'} + # + # NOTICE: Results key order is NOT assured because of the Hash's each method does not assure the order. + # If you want to get the ordered results, please pass a Orderd Hash as the first argument. (ex. ActiveSupport::OrderedHash) + # + # + # If you use a underscored key, like + # {:instance_type => ['t1.micro', 'm1.small']} + # + # The :instance_type key is automatically fixed to 'instance-type', so you should get + # {"Filter.1.Name"=>'instance_type', 'Filter.1.Value.1'=>'t1.micro', 'Filter.1.Value.2'=>'m1.small'} + # + def filterlist(filters) + return {} if filters.nil? + raise ArgumentError, "filters must be a Hash" unless filters.is_a?(Hash) + + filters.each do |key, val| + unless key.is_a?(String) || key.is_a?(Symbol) + raise ArgumentError, ":#{key} must be a String or Symbol (actual: #{key.class.name})" + end + unless val == true || val == false || val.is_a?(Symbol) || val.is_a?(String) || val.is_a?(Array) + raise ArgumentError, "#{key}[#{val}] must be a String or Symbol or Boolean or Array (actual: #{val.class.name})" + end + end + + params = {} + filters.each_with_index do |key_val, idx| + params["Filter.#{idx+1}.Name"] = key_val[0].to_s.gsub(/_/, '-') + Array(key_val[1]).each_with_index do |value, i| + params["Filter.#{idx+1}.Value.#{i+1}"] = value.to_s + end + end + params + end + # Make the connection to AWS EC2 passing in our request. This is generally called from # within a 'Response' class object or one of its sub-classes so the response is interpreted # in its proper context. See lib/EC2/responses.rb From 75aede1f31917d3f33b0a37946fc209db0cf1428 Mon Sep 17 00:00:00 2001 From: Norihito Yoshioka Date: Thu, 26 May 2011 12:23:06 +0900 Subject: [PATCH 14/24] Implement the describe_regions method. Support filter arguments for the describe_availability_zones method. --- lib/AWS/EC2/availability_zones.rb | 50 ++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/lib/AWS/EC2/availability_zones.rb b/lib/AWS/EC2/availability_zones.rb index 6775120..0dbc011 100644 --- a/lib/AWS/EC2/availability_zones.rb +++ b/lib/AWS/EC2/availability_zones.rb @@ -2,6 +2,24 @@ module AWS module EC2 class Base < AWS::Base + # describe_availability_zones filters list + DESCRIBE_AVAILABILITY_ZONES_FILTERS = [ + :message, + :region_name, + :state, + :zone_name + ] + + # describe_availability_zones alternative filter names + DESCRIBE_AVAILABILITY_ZONES_FILTER_ALTERNATIVES = { :zone_name_filter => :zone_name, } + + # describe_regions filter list + DESCRIBE_REGIONS_FILTERS = [:endpoint, :region_name] + + # describe_regions alternative filter names + DESCRIBE_REGIONS_FILTER_ALTERNAITVES = { :region_name_filter => :region_name, } + + # The DescribeAvailabilityZones operation describes availability zones that are currently # available to the account and their states. # @@ -11,16 +29,40 @@ class Base < AWS::Base # def describe_availability_zones( options = {} ) options = { :zone_name => [] }.merge(options) - params = pathlist("ZoneName", options[:zone_name] ) + params = pathlist("ZoneName", options.delete(:zone_name)) + + DESCRIBE_AVAILABILITY_ZONES_FILTER_ALTERNATIVES.each do |alternative_key, original_key| + next unless options.include?(alternative_key) + options[original_key] = options.delete(alternative_key) + end + + invalid_filters = options.keys - DESCRIBE_AVAILABILITY_ZONES_FILTERS + raise ArgumentError, "invalid filter(s): #{invalid_filters.join(', ')}" if invalid_filters.any? + params.merge!(filterlist(options)) return response_generator(:action => "DescribeAvailabilityZones", :params => params) end - # Not yet implemented + # The DescribeRegions operation describes regions. + # + # An optional list of region names can be passed. # - # @todo Implement this method + # @option options [optional, String] :region_name ([]) an Array of region names + # @option options [optional, String] :endpoint ([]) an Array of endpoint + # @option options [optional, String] :region_name_filter ([]) an Array of region names # def describe_regions( options = {} ) - raise "Not yet implemented" + options = { :zone_name => [] }.merge(options) + params = pathlist("ZoneName", options.delete(:zone_name)) + + DESCRIBE_REGIONS_FILTER_ALTERNAITVES.each do |alternative_key, original_key| + next unless options.include?(alternative_key) + options[original_key] = options.delete(alternative_key) + end + + invalid_filters = options.keys - DESCRIBE_REGIONS_FILTERS + raise ArgumentError, "invalid filter(s): #{invalid_filters.join(', ')}" if invalid_filters.any? + params.merge!(filterlist(options)) + return response_generator(:action => "DescribeRegions", :params => params) end end From 0369045021b67341feb457f7a63fddd8d6b888a0 Mon Sep 17 00:00:00 2001 From: Norihito Yoshioka Date: Thu, 26 May 2011 12:30:10 +0900 Subject: [PATCH 15/24] Support filter arguments for the describe_images method. --- lib/AWS/EC2/images.rb | 54 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/lib/AWS/EC2/images.rb b/lib/AWS/EC2/images.rb index e579af9..05caf8c 100644 --- a/lib/AWS/EC2/images.rb +++ b/lib/AWS/EC2/images.rb @@ -3,6 +3,43 @@ module EC2 class Base < AWS::Base + # describe_images filters list + DESCRIBE_IMAGES_FILTERS = [ + :architecture, + 'block_device_mapping.delete_on_termination', + 'block_device_mapping.device_name', + 'block_device_mapping.snapshot_id', + 'block_device_mapping.volume_size', + :description, + :image_id, + :image_type, + :is_public, + :kernel_id, + :manifest_location, + :name, + :owner_alias, + :owner_id, + :platform, + :product_code, + :ramdisk_id, + :root_device_name, + :root_device_type, + :state, + :state_reason_code, + :state_reason_message, + :tag_key, + :tag_value, + 'tag:key', + :virtualization_type, + :hypervisor + ] + + # describe_images alternative filter name mappings + DESCRIBE_IMAGES_FILTER_ALTERNATIVES = { + :image_id_filter => :image_id, + :owner_id_filter => :owner_id, + } + # Creates an AMI that uses an Amazon EBS root device from a "running" or "stopped" instance. # # AMIs that use an Amazon EBS root device boot faster than AMIs that use instance stores. @@ -144,16 +181,21 @@ def register_image( options = {} ) # def describe_images( options = {} ) options = { :image_id => [], :owner_id => [], :executable_by => [] }.merge(options) - params = pathlist( "ImageId", options[:image_id] ) - params.merge!(pathlist( "Owner", options[:owner_id] )) - params.merge!(pathlist( "ExecutableBy", options[:executable_by] )) - if options[:filter] - params.merge!(pathkvlist('Filter', options[:filter], 'Name', 'Value', {})) + params = pathlist( "ImageId", options.delete(:image_id) ) + params.merge!(pathlist( "Owner", options.delete(:owner_id) )) + params.merge!(pathlist( "ExecutableBy", options.delete(:executable_by) )) + + DESCRIBE_IMAGES_FILTER_ALTERNATIVES.each do |alternative_key, original_key| + next unless options.include?(alternative_key) + options[original_key] = options.delete(alternative_key) end + + invalid_filters = options.keys - DESCRIBE_IMAGES_FILTERS + raise ArgumentError, "invalid filter(s): #{invalid_filters.join(', ')}" if invalid_filters.any? + params.merge!(filterlist(options)) return response_generator(:action => "DescribeImages", :params => params) end - end end end From 22f260ed8723ed0012dcb894ebfc3ab38640d963 Mon Sep 17 00:00:00 2001 From: Norihito Yoshioka Date: Thu, 26 May 2011 12:35:57 +0900 Subject: [PATCH 16/24] Support filter arguments for the describe_volumes method. --- lib/AWS/EC2/volumes.rb | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/AWS/EC2/volumes.rb b/lib/AWS/EC2/volumes.rb index 60ef6e6..594b4e7 100644 --- a/lib/AWS/EC2/volumes.rb +++ b/lib/AWS/EC2/volumes.rb @@ -2,6 +2,28 @@ module AWS module EC2 class Base < AWS::Base + # describe_volumes filters list + DESCRIBE_VOLUMES_FILTERS = [ + 'attachment.attach-time', + 'attachment.delete-on-termination', + 'attachment.device', + 'attachment.instance-id', + 'attachment.status', + :availability_zone, + :create_time, + :size, + :snapshot_id, + :status, + :tag_key, + :tag_value, + 'tag:key', + :volume_id + ] + + # describe_volumes alternative filter name mappings + DESCRIBE_VOLUMES_FILTER_ALTERNATIVES = { + :volume_id_filter => :volume_id + } # The DescribeVolumes operation lists one or more Amazon EBS volumes that you own, If you do not specify any volumes, Amazon EBS returns all volumes that you own. # @@ -9,10 +31,17 @@ class Base < AWS::Base # def describe_volumes( options = {} ) options = { :volume_id => [] }.merge(options) - params = pathlist("VolumeId", options[:volume_id] ) - if options[:filter] - params.merge!(pathkvlist('Filter', options[:filter], 'Name', 'Value', {})) + params = pathlist("VolumeId", options.delete(:volume_id)) + + DESCRIBE_VOLUMES_FILTER_ALTERNATIVES.each do |alternative_key, original_key| + next unless options.include?(alternative_key) + options[original_key] = options.delete(alternative_key) end + + invalid_filters = options.keys - DESCRIBE_VOLUMES_FILTERS + raise ArgumentError, "invalid filter(s): #{invalid_filters.join(', ')}" if invalid_filters.any? + params.merge!(filterlist(options)) + return response_generator(:action => "DescribeVolumes", :params => params) end From 65702b44fd16df16a2ca3e43407af8c42db628df Mon Sep 17 00:00:00 2001 From: Norihito Yoshioka Date: Thu, 26 May 2011 12:53:13 +0900 Subject: [PATCH 17/24] Support filter arguments for the describe_instances method. --- lib/AWS/EC2/instances.rb | 69 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/lib/AWS/EC2/instances.rb b/lib/AWS/EC2/instances.rb index cc73164..14a5c8c 100644 --- a/lib/AWS/EC2/instances.rb +++ b/lib/AWS/EC2/instances.rb @@ -2,6 +2,63 @@ module AWS module EC2 class Base < AWS::Base + # describe_instances filters list + DESCRIBE_INSTANCES_FILTERS = [ + :architecture, + :availability_zone, + 'block-device-mapping.attach-time', + 'block-device-mapping.delete-on-termination', + 'block-device-mapping.device-name', + 'block-device-mapping.status', + 'block-device-mapping.volume-id', + :client_token, + :dns_name, + :group_id, + :group_name, + :image_id, + :instance_id, + :instance_lifecycle, + :instance_state_code, + :instance_state_name, + :instance_type, + 'instance.group-id', + 'instance.group-name', + :ip_address, + :kernel_id, + :key_name, + :launch_index, + :launch_time, + :monitoring_state, + :owner_id, + :placement_group_name, + :platform, + :private_dns_name, + :private_ip_address, + :product_code, + :ramdisk_id, + :reason, + :requester_id, + :reservation_id, + :root_device_name, + :root_device_type, + :source_dest_check, + :spot_instance_request_id, + :state_reason_code, + :state_reason_message, + :subnet_id, + :tag_key, + :tag_value, + 'tag:key', + :virtualization_type, + :vpc_id, + :hypervisor + ] + + # describe_instances alternative filter name mappings + DESCRIBE_INSTANCES_FILTER_ALTERNATIVES = { + :instance_id_filter => :instance_id, + } + # Launches a specified number of instances of an AMI for which you have permissions. # # Amazon API Docs : HTML[http://docs.amazonwebservices.com/AWSEC2/2009-10-31/APIReference/index.html?ApiReference-query-RunInstances.html] @@ -89,10 +146,16 @@ def run_instances( options = {} ) # def describe_instances( options = {} ) options = { :instance_id => [] }.merge(options) - params = pathlist("InstanceId", options[:instance_id]) - if options[:filter] - params.merge!(pathkvlist('Filter', options[:filter], 'Name', 'Value', {})) + params = pathlist("InstanceId", options.delete(:instance_id)) + + DESCRIBE_INSTANCES_FILTER_ALTERNATIVES.each do |alternative_key, original_key| + next unless options.include?(alternative_key) + options[original_key] = options.delete(alternative_key) end + + invalid_filters = options.keys - DESCRIBE_INSTANCES_FILTERS + raise ArgumentError, "invalid filter(s): #{invalid_filters.join(', ')}" if invalid_filters.any? + params.merge!(filterlist(options)) return response_generator(:action => "DescribeInstances", :params => params) end From 90e597cbd3cf672f6940295176b3cf5f30270678 Mon Sep 17 00:00:00 2001 From: Norihito Yoshioka Date: Thu, 26 May 2011 12:57:43 +0900 Subject: [PATCH 18/24] Support filter arguments for the describe_snapshots method. --- lib/AWS/EC2/snapshots.rb | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lib/AWS/EC2/snapshots.rb b/lib/AWS/EC2/snapshots.rb index e339517..f60b0b9 100644 --- a/lib/AWS/EC2/snapshots.rb +++ b/lib/AWS/EC2/snapshots.rb @@ -3,6 +3,26 @@ module EC2 class Base < AWS::Base + # describe_snapshots filters list + DESCRIBE_SNAPSHOTS_FILTERS = [ + :description, + :owner_alias, + :owner_id, + :progress, + :snapshot_id, + :start_time, + :status, + :tag_key, + :tag_value, + 'tag:key', + :volume_id, + :volume_size + ] + + # describe_sanpshots alternative filter name mappings + DESCRIBE_SNAPSHOTS_FILTER_ALTERNATIVES = { + :snapshot_id_filter => :snapshot_id + } # The DescribeSnapshots operation describes the status of Amazon EBS snapshots. # @@ -15,6 +35,19 @@ def describe_snapshots( options = {} ) params.merge!(pathlist("SnapshotId", options[:snapshot_id] )) unless options[:snapshot_id].nil? || options[:snapshot_id] == [] params["RestorableBy"] = options[:restorable_by] unless options[:restorable_by].nil? params["Owner"] = options[:owner] unless options[:owner].nil? + options.delete(:snapshot_id) + options.delete(:restorable_by) + options.delete(:owner) + + DESCRIBE_SNAPSHOTS_FILTER_ALTERNATIVES.each do |alternative_key, original_key| + next unless options.include?(alternative_key) + options[original_key] = options.delete(alternative_key) + end + + invalid_filters = options.keys - DESCRIBE_SNAPSHOTS_FILTERS + raise ArgumentError, "invalid filter(s): #{invalid_filters.join(', ')}" if invalid_filters.any? + params.merge!(filterlist(options)) + return response_generator(:action => "DescribeSnapshots", :params => params) end From fa394458a3023e5fb6decca9d21781b0d576b68b Mon Sep 17 00:00:00 2001 From: Norihito Yoshioka Date: Thu, 26 May 2011 13:01:53 +0900 Subject: [PATCH 19/24] Bump the version number to 0.9.18. --- Gemfile.lock | 2 +- VERSION | 2 +- lib/AWS/version.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index b792133..25a8cf3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - amazon-ec2 (0.9.17) + amazon-ec2 (0.9.18) xml-simple (>= 1.0.12) GEM diff --git a/VERSION b/VERSION index 4148992..7a50b68 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.17 +0.9.18 diff --git a/lib/AWS/version.rb b/lib/AWS/version.rb index 32062d4..57b08b3 100644 --- a/lib/AWS/version.rb +++ b/lib/AWS/version.rb @@ -1,3 +1,3 @@ module AWS - VERSION = "0.9.17" + VERSION = "0.9.18" end From 8185bf2bacdb9c290263b69e8f3525fa40ec99b7 Mon Sep 17 00:00:00 2001 From: Norihito Yoshioka Date: Thu, 26 May 2011 14:16:59 +0900 Subject: [PATCH 20/24] Fix a bug that the describe_snapshots rewrites the argument. --- lib/AWS/EC2/snapshots.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/AWS/EC2/snapshots.rb b/lib/AWS/EC2/snapshots.rb index f60b0b9..a9c4b58 100644 --- a/lib/AWS/EC2/snapshots.rb +++ b/lib/AWS/EC2/snapshots.rb @@ -31,10 +31,14 @@ class Base < AWS::Base # @option options [optional,String] :restorable_by ('') Account ID of a user that can create volumes from the snapshot. # def describe_snapshots( options = {} ) + options ||= {} + options = options.dup + params = {} params.merge!(pathlist("SnapshotId", options[:snapshot_id] )) unless options[:snapshot_id].nil? || options[:snapshot_id] == [] params["RestorableBy"] = options[:restorable_by] unless options[:restorable_by].nil? params["Owner"] = options[:owner] unless options[:owner].nil? + options.delete(:snapshot_id) options.delete(:restorable_by) options.delete(:owner) From a40e5f0c00b505dc034896f25b28bc8de8992d36 Mon Sep 17 00:00:00 2001 From: Norihito Yoshioka Date: Tue, 31 May 2011 11:40:03 +0900 Subject: [PATCH 21/24] =?UTF-8?q?DeleteSecurityGroup=20API=20=E3=81=AE?= =?UTF-8?q?=E5=BC=95=E6=95=B0=E3=81=AB=20:group=5Fid=20=E3=82=92=E5=8F=97?= =?UTF-8?q?=E3=81=91=E4=BB=98=E3=81=91=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E3=81=97=E3=81=9F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION | 2 +- lib/AWS/EC2/security_groups.rb | 14 +++++++++++--- lib/AWS/version.rb | 2 +- test/test_EC2_security_groups.rb | 14 ++++++++++++++ 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index 7a50b68..f752268 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.18 +0.9.19 diff --git a/lib/AWS/EC2/security_groups.rb b/lib/AWS/EC2/security_groups.rb index f6f8575..c0ab276 100644 --- a/lib/AWS/EC2/security_groups.rb +++ b/lib/AWS/EC2/security_groups.rb @@ -59,9 +59,17 @@ def describe_security_groups( options = {} ) # @option options [String] :group_name ("") # def delete_security_group( options = {} ) - options = { :group_name => "" }.merge(options) - raise ArgumentError, "No :group_name provided" if options[:group_name].nil? || options[:group_name].empty? - params = { "GroupName" => options[:group_name] } + options ||= {} + options = options.dup + if (options[:group_id].nil? || options[:group_id].empty?) && (options[:group_name].nil? || options[:group_name].empty?) + raise ArgumentError, "No :group_id or :group_name provided" + end + if !options[:group_id].nil? && !options[:group_id].empty? && !options[:group_name].nil? && !options[:group_name].empty? + raise ArgumentError, ":group_id and :group_name both provided" + end + params = {} + params["GroupId"] = options[:group_id] unless options[:group_id].nil? || options[:group_id].empty? + params["GroupName"] = options[:group_name] unless options[:group_name].nil? || options[:group_name].empty? return response_generator(:action => "DeleteSecurityGroup", :params => params) end diff --git a/lib/AWS/version.rb b/lib/AWS/version.rb index 57b08b3..def0d40 100644 --- a/lib/AWS/version.rb +++ b/lib/AWS/version.rb @@ -1,3 +1,3 @@ module AWS - VERSION = "0.9.18" + VERSION = "0.9.19" end diff --git a/test/test_EC2_security_groups.rb b/test/test_EC2_security_groups.rb index b25fe99..9eebf4c 100644 --- a/test/test_EC2_security_groups.rb +++ b/test/test_EC2_security_groups.rb @@ -112,6 +112,13 @@ end + specify "should be able to be deleted if group_id provided" do + @ec2.stubs(:make_request).with('DeleteSecurityGroup', {"GroupId"=>"sg-00000001"}). + returns stub(:body => @delete_security_group_response_body, :is_a? => true) + @ec2.delete_security_group( :group_id => "sg-00000001" ).should.be.an.instance_of Hash + end + + specify "method delete_security_group should reject bad arguments" do @ec2.stubs(:make_request).with('DeleteSecurityGroup', {"GroupName"=>"WebServers"}). returns stub(:body => @delete_security_group_response_body, :is_a? => true) @@ -122,6 +129,13 @@ # :group_name can't be nil or empty lambda { @ec2.delete_security_group( :group_name => "" ) }.should.raise(AWS::ArgumentError) lambda { @ec2.delete_security_group( :group_name => nil ) }.should.raise(AWS::ArgumentError) + + # :group_id can't be nil or empty + lambda { @ec2.delete_security_group( :group_id => "" ) }.should.raise(AWS::ArgumentError) + lambda { @ec2.delete_security_group( :group_id => nil ) }.should.raise(AWS::ArgumentError) + + # can't specify :group_id and :group_name at the same time + lambda { @ec2.delete_security_group( :group_id => "sg-00000001", :group_name => "WebServers" ) }.should.raise(AWS::ArgumentError) end From 99210056687c7ecdede13006ded45fe93f436526 Mon Sep 17 00:00:00 2001 From: Junya Ogura Date: Fri, 8 Jul 2011 15:21:05 +0900 Subject: [PATCH 22/24] =?UTF-8?q?CloudWatch=20API=E3=81=AE=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C=E3=83=90=E3=83=BC=E3=82=B8=E3=83=A7=E3=83=B3=E3=82=92?= =?UTF-8?q?2010-05-15=E3=81=8B=E3=82=892010-08-01=E3=81=AB=E5=A4=89?= =?UTF-8?q?=E6=9B=B4=E3=81=97=E3=81=9F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile.lock | 5 ++-- lib/AWS/Cloudwatch.rb | 4 +-- lib/AWS/Cloudwatch/monitoring.rb | 43 +++++++++++++++++++++++++------- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 25a8cf3..4e9fd87 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - amazon-ec2 (0.9.18) + amazon-ec2 (0.9.19) xml-simple (>= 1.0.12) GEM @@ -13,7 +13,7 @@ GEM rcov (0.9.9) test-spec (0.10.0) test-unit (2.3.0) - xml-simple (1.0.15) + xml-simple (1.1.0) yard (0.6.8) PLATFORMS @@ -27,5 +27,4 @@ DEPENDENCIES rcov (>= 0.9.9) test-spec (>= 0.10.0) test-unit (>= 2.1.2) - xml-simple (>= 1.0.12) yard (>= 0.6.2) diff --git a/lib/AWS/Cloudwatch.rb b/lib/AWS/Cloudwatch.rb index 1db4266..dcd6418 100644 --- a/lib/AWS/Cloudwatch.rb +++ b/lib/AWS/Cloudwatch.rb @@ -14,7 +14,7 @@ module Cloudwatch DEFAULT_HOST = 'monitoring.amazonaws.com' end - API_VERSION = '2009-05-15' + API_VERSION = '2010-08-01' class Base < AWS::Base def api_version @@ -27,4 +27,4 @@ def default_host end end -end \ No newline at end of file +end diff --git a/lib/AWS/Cloudwatch/monitoring.rb b/lib/AWS/Cloudwatch/monitoring.rb index 28778c9..5071b70 100644 --- a/lib/AWS/Cloudwatch/monitoring.rb +++ b/lib/AWS/Cloudwatch/monitoring.rb @@ -6,12 +6,37 @@ class Base < AWS::Base # account. To get further information from the metrics, you'll then need to # call get_metric_statistics. # - # @option options [String] :next_token Next token + # @option options [String] :dimensions A list of dimensions to filter against. (InstanceId=i-00000001,VolumeId=vol-0000001) + # @option options [String] :metric_name The name of the metric to filter against. + # @option options [String] :namespace The namespace to filter against. + # @option options [String] :next_token The token returned by a previous call to indicate that there is more data available. + # @see http://docs.amazonwebservices.com/AmazonCloudWatch/latest/APIReference/API_ListMetrics.html def list_metrics( options ={} ) - options = { :next_token => nil }.merge(options) + options = { + :dimensions => nil, + :metric_name => nil, + :next_token => nil, + :namespace => nil, + }.merge(options) params = {} + params['MetricName'] = options[:metric_name] if options[:metric_name] params['NextToken'] = options[:next_token] if options[:next_token] + params['Namespace'] = options[:namespace] if options[:namespace] + + # FDT: Fix statistics and dimensions values + if !(options[:dimensions].nil? || options[:dimensions].empty?) + dims_params = {} + i = 1 + options[:dimensions].split(',').each{ |dimension| + dimension_var = dimension.split('=') + dims_params = dims_params.merge!( "Dimensions.member.#{i}.Name" => "#{dimension_var[0]}", "Dimensions.member.#{i}.Value" => "#{dimension_var[1]}" ) + i += 1 + } + raise ArgumentError, "Maximum of 10 items in the :dimensions" if i > 10 + + params.merge!( dims_params ) + end return response_generator(:action => 'ListMetrics', :params => params) end @@ -19,7 +44,7 @@ def list_metrics( options ={} ) # get_metric_statistics pulls a hashed array from Cloudwatch with the stats # of your requested metric. # Once you get the data out, if you assign the results into an object like: - # res = @mon.get_metric_statistics(:measure_name => 'RequestCount', \ + # res = @mon.get_metric_statistics(:metric_name => 'RequestCount', \ # :statistics => 'Average', :namespace => 'AWS/ELB') # # This call gets the average request count against your ELB at each sampling period @@ -30,18 +55,18 @@ def list_metrics( options ={} ) # @option options [String] :custom_unit (nil) not currently available, placeholder # @option options [String] :dimensions (nil) Option to filter your data on. Check the developer guide # @option options [Time] :end_time (Time.now()) Outer bound of the date range you want to view - # @option options [String] :measure_name (nil) The measure you want to check. Must correspond to - # => provided options - # @option options [String] :namespace ('AWS/EC2') The namespace of your measure_name. Currently, 'AWS/EC2' and 'AWS/ELB' are available + # @option options [String] :metric_name (nil) The name of the metric. + # @option options [String] :namespace ('AWS/EC2') The namespace of your metric_name. Currently, 'AWS/EC2' and 'AWS/ELB' are available # @option options [Integer] :period (60) Granularity in seconds of the returned datapoints. Multiples of 60 only # @option options [String] :statistics (nil) The statistics to be returned for your metric. See the developer guide for valid options. Required. # @option options [Time] :start_time (Time.now() - 86400) Inner bound of the date range you want to view. Defaults to 24 hours ago # @option options [String] :unit (nil) Standard unit for a given Measure. See the developer guide for valid options. + # @see http://docs.amazonwebservices.com/AmazonCloudWatch/latest/APIReference/API_GetMetricStatistics.html def get_metric_statistics ( options ={} ) options = { :custom_unit => nil, :dimensions => nil, :end_time => Time.now(), #req - :measure_name => "", #req + :metric_name => "", #req :namespace => "AWS/EC2", :period => 60, :statistics => "", # req @@ -53,13 +78,13 @@ def get_metric_statistics ( options ={} ) raise ArgumentError, ":start_time must be provided" if options[:start_time].nil? raise ArgumentError, ":start_time must be a Time object" if options[:start_time].class != Time raise ArgumentError, ":start_time must be before :end_time" if options[:start_time] > options[:end_time] - raise ArgumentError, ":measure_name must be provided" if options[:measure_name].nil? || options[:measure_name].empty? + raise ArgumentError, ":metric_name must be provided" if options[:metric_name].nil? || options[:metric_name].empty? raise ArgumentError, ":statistics must be provided" if options[:statistics].nil? || options[:statistics].empty? params = { "CustomUnit" => options[:custom_unit], "EndTime" => options[:end_time].iso8601, - "MeasureName" => options[:measure_name], + "MetricName" => options[:metric_name], "Namespace" => options[:namespace], "Period" => options[:period].to_s, "StartTime" => options[:start_time].iso8601, From 5bc36217f972946073b162ad78aab7070ea2c48e Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Fri, 8 Jun 2012 15:09:09 +0900 Subject: [PATCH 23/24] add m1.medium instance type --- ChangeLog | 3 +++ README.md | 2 +- lib/AWS/EC2/instances.rb | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 89903c1..1186cae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +=== x.x.x 2012-06-08 + * add EC@ instance_type "m1.medium". (mitsuharu_odagiri) + === x.x.x 2011-05-08 * add NextToken support to Cloudwatch ListMetrics API. (juno) * added error handling for Cloudwatch. (juno) diff --git a/README.md b/README.md index 5339131..8b291b2 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ amazon-ec2カスタマイズ版 $ cd /path/to/rails-app $ rm -rf vendor/gems/amazon-ec2-0.9.15 # => 古いamazon-ec2ライブラリを削除する - $ gem /path/to/amazon-ec2-0.9.15.gem --target vendor/gems # => 新しいamazon-ec2ライブラリを展開する + $ gem unpack /path/to/amazon-ec2-0.9.15.gem --target vendor/gems # => 新しいamazon-ec2ライブラリを展開する $ ls vendor/gems amazon-ec2-0.9.15 diff --git a/lib/AWS/EC2/instances.rb b/lib/AWS/EC2/instances.rb index 14a5c8c..78ba661 100644 --- a/lib/AWS/EC2/instances.rb +++ b/lib/AWS/EC2/instances.rb @@ -94,7 +94,7 @@ def run_instances( options = {} ) raise ArgumentError, ":image_id must be provided" if options[:image_id].nil? || options[:image_id].empty? raise ArgumentError, ":min_count is not valid" unless options[:min_count].to_i > 0 raise ArgumentError, ":max_count is not valid or must be >= :min_count" unless options[:max_count].to_i > 0 && options[:max_count].to_i >= options[:min_count].to_i - raise ArgumentError, ":instance_type must specify a valid instance type" unless options[:instance_type].nil? || ["t1.micro", "m1.small", "m1.large", "m1.xlarge", "m2.xlarge", "c1.medium", "c1.xlarge", "m2.2xlarge", "m2.4xlarge", "cc1.4xlarge"].include?(options[:instance_type]) + raise ArgumentError, ":instance_type must specify a valid instance type" unless options[:instance_type].nil? || ["t1.micro", "m1.small", "m1.medium", "m1.large", "m1.xlarge", "m2.xlarge", "c1.medium", "c1.xlarge", "m2.2xlarge", "m2.4xlarge", "cc1.4xlarge"].include?(options[:instance_type]) raise ArgumentError, ":monitoring_enabled must be 'true' or 'false'" unless options[:monitoring_enabled].nil? || [true, false].include?(options[:monitoring_enabled]) raise ArgumentError, ":disable_api_termination must be 'true' or 'false'" unless options[:disable_api_termination].nil? || [true, false].include?(options[:disable_api_termination]) raise ArgumentError, ":instance_initiated_shutdown_behavior must be 'stop' or 'terminate'" unless options[:instance_initiated_shutdown_behavior].nil? || ["stop", "terminate"].include?(options[:instance_initiated_shutdown_behavior]) From b0552c6ebe38709ba99717fc8d00552a01f5e266 Mon Sep 17 00:00:00 2001 From: EC2 Default User Date: Fri, 8 Jun 2012 17:36:48 +0900 Subject: [PATCH 24/24] add m1.medium EC2 instance type --- ChangeLog | 4 ++-- README.md | 2 +- test/test_EC2_instances.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1186cae..dd5fd05 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,5 @@ -=== x.x.x 2012-06-08 - * add EC@ instance_type "m1.medium". (mitsuharu_odagiri) +=== x.x.x 2011-06-08 + * add EC2 instance_type "m1.medium". (mitsuharu_odagiri) === x.x.x 2011-05-08 * add NextToken support to Cloudwatch ListMetrics API. (juno) diff --git a/README.md b/README.md index 8b291b2..5339131 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ amazon-ec2カスタマイズ版 $ cd /path/to/rails-app $ rm -rf vendor/gems/amazon-ec2-0.9.15 # => 古いamazon-ec2ライブラリを削除する - $ gem unpack /path/to/amazon-ec2-0.9.15.gem --target vendor/gems # => 新しいamazon-ec2ライブラリを展開する + $ gem /path/to/amazon-ec2-0.9.15.gem --target vendor/gems # => 新しいamazon-ec2ライブラリを展開する $ ls vendor/gems amazon-ec2-0.9.15 diff --git a/test/test_EC2_instances.rb b/test/test_EC2_instances.rb index 7660f58..b9699ca 100644 --- a/test/test_EC2_instances.rb +++ b/test/test_EC2_instances.rb @@ -332,7 +332,7 @@ # :instance_type - ["t1.micro", "m1.small", "m1.large", "m1.xlarge", "m2.xlarge", "c1.medium", "c1.xlarge", "m2.2xlarge", "m2.4xlarge", "cc1.4xlarge"].each do |type| + ["t1.micro", "m1.small", "m1.medium", "m1.large", "m1.xlarge", "m2.xlarge", "c1.medium", "c1.xlarge", "m2.2xlarge", "m2.4xlarge", "cc1.4xlarge"].each do |type| @ec2.stubs(:make_request).with('RunInstances', "ImageId" => "ami-60a54009", "MinCount" => '1', "MaxCount" => '1', "InstanceType" => type). returns stub(:body => @run_instances_response_body, :is_a? => true) lambda { @ec2.run_instances( :image_id => "ami-60a54009", :instance_type => type ) }.should.not.raise(AWS::ArgumentError)