diff --git a/ChangeLog b/ChangeLog index 2b884da..dd5fd05 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +=== 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) + * 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 c55d84b..4e9fd87 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,31 +1,30 @@ PATH remote: . specs: - amazon-ec2 (0.9.17) + amazon-ec2 (0.9.19) xml-simple (>= 1.0.12) GEM remote: http://rubygems.org/ specs: - mocha (0.9.9) - rake - perftools.rb (0.5.4) - rake (0.8.7) + awesome_print (0.3.2) + 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.1.0) + yard (0.6.8) PLATFORMS ruby DEPENDENCIES amazon-ec2! + awesome_print mocha (>= 0.9.9) perftools.rb (>= 0.5.4) 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/README.md b/README.md new file mode 100644 index 0000000..5339131 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +amazon-ec2カスタマイズ版 +==== + +コードに変更を加えた場合は`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 index 74ac5a4..9d09db7 100644 --- a/README.rdoc +++ b/README.rdoc @@ -345,4 +345,3 @@ Please follow these steps if you want to send a patch or a GitHub pull request: Enjoy! Glenn Rempe - diff --git a/VERSION b/VERSION index 5d11b14..f752268 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.15 +0.9.19 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.rb b/lib/AWS.rb index 23afd2c..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 @@ -291,7 +335,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) @@ -346,17 +390,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/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 538debe..5071b70 100644 --- a/lib/AWS/Cloudwatch/monitoring.rb +++ b/lib/AWS/Cloudwatch/monitoring.rb @@ -6,15 +6,45 @@ 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] :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 = { + :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 # 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 @@ -25,18 +55,18 @@ def list_metrics # @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 @@ -48,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, 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/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 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 diff --git a/lib/AWS/EC2/instances.rb b/lib/AWS/EC2/instances.rb index 71f7653..78ba661 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] @@ -37,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]) @@ -85,11 +142,20 @@ 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]) + 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 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/lib/AWS/EC2/security_groups.rb b/lib/AWS/EC2/security_groups.rb index 53073f9..c0ab276 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 @@ -51,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 @@ -74,35 +90,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 +153,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/lib/AWS/EC2/snapshots.rb b/lib/AWS/EC2/snapshots.rb index e339517..a9c4b58 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. # @@ -11,10 +31,27 @@ 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) + + 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 diff --git a/lib/AWS/EC2/volumes.rb b/lib/AWS/EC2/volumes.rb index dfdbece..594b4e7 100644 --- a/lib/AWS/EC2/volumes.rb +++ b/lib/AWS/EC2/volumes.rb @@ -2,14 +2,46 @@ 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. # - # @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] ) + 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 diff --git a/lib/AWS/version.rb b/lib/AWS/version.rb index 32062d4..def0d40 100644 --- a/lib/AWS/version.rb +++ b/lib/AWS/version.rb @@ -1,3 +1,3 @@ module AWS - VERSION = "0.9.17" + VERSION = "0.9.19" end 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 new file mode 100644 index 0000000..5a5ec46 --- /dev/null +++ b/test/test_Cloudwatch.rb @@ -0,0 +1,31 @@ +require File.expand_path(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 + + 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 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..b9699ca 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 @@ -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) diff --git a/test/test_EC2_keypairs.rb b/test/test_EC2_keypairs.rb index f56254b..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 @@ -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 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 d44235b..9eebf4c 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 @@ -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 @@ -162,46 +176,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 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'